Usage
Get number of trials by method, day, and user:
db <- db_connect()
counts <- dbGetQuery(db,"SELECT r.method AS 'method',
DATE(r.date + 3600, 'unixepoch') AS 'doy',
r.user_id AS 'user',
COUNT(*) AS 'trials'
FROM 'responses_noduplicates' r
GROUP BY r.method,
DATE(r.date + 3600, 'unixepoch'),
r.user_id
")
db_disconnect(db)
setDT(counts)
Add a school year column (cutoff date: 1 August):
counts[, doy_posix := as.POSIXct(doy)]
counts[, school_year := ifelse(doy_posix < "2019-08-01", "18/19", "19/20")]
Add more sensible course names:
counts[, course := ifelse(method == "Grandes Lignes", "French", ifelse(method == "Stepping Stones", "English", "German"))]
Total number of unique users by course and school year
counts[, .(unique_users = length(unique(user))), by = .(course, school_year)]
There was some overlap between courses/school years, so we can’t simply add these numbers.
By course:
counts[, .(unique_users = length(unique(user))), by = .(course)]
By school year in the French and English courses:
counts[course %in% c("French", "English"), .(unique_users = length(unique(user))), by = .(school_year)]
And total number of unique users in the French and English sample across both years:
counts[course %in% c("French", "English"), .(unique_users = length(unique(user)))]
Total number of trials by course
counts[, .(total_trials = sum(trials)), by = .(course, school_year)]
Number of trials by user
counts[, trials_user := sum(trials), by = .(course, user)]
ggplot(counts, aes(x = trials_user)) +
facet_wrap(~course, ncol = 1, scales = "free_y") +
geom_histogram(binwidth = 100) +
labs(x = "Number of trials by user",
y = NULL) +
theme_paper

Number of unique days by user
ggplot(counts[, .(N = length(unique(doy_posix))), by = c("user", "course")], aes(x = N)) +
facet_grid(course ~ ., scales = "free_y") +
geom_histogram(binwidth = 1) +
labs(x = "Aantal dagen met leeractiviteit",
y = "Aantal leerlingen") +
theme_paper

Total number of trials by day
Interpolate missing days:
doy_posix <- seq.POSIXt(from = counts[,min(doy_posix)], to = counts[,max(doy_posix)], by = "DSTday")
course <- counts[,unique(course)]
dates <- CJ(doy_posix, course)
counts <- merge(counts, dates, by = c("doy_posix", "course"), all = TRUE)
Count trials by day:
counts[, trials_total := sum(trials, na.rm = TRUE), by = .(course, doy_posix)]
counts_by_day <- counts[, .(trials_total = sum(trials, na.rm = TRUE)), by = .(course, doy_posix)]
ggplot(counts_by_day[course %in% c("English", "French"),],
aes(x = doy_posix, y = trials_total, colour = course)) +
geom_line() +
scale_x_datetime(date_breaks = "3 months", date_labels = "%e %b %Y") +
scale_y_continuous(labels = number_format) +
labs(x = NULL,
y = "Number of trials per day",
colour = "Course") +
theme_paper

Total number of trials by week
Use cut.Date() to bin dates by week. Each day is assigned the date of the most recent Monday.
counts_by_day[, doy_posix_week := cut.POSIXt(doy_posix, "week")]
counts_by_day[, trials_total_week := sum(trials_total, na.rm = TRUE), by = .(course, doy_posix_week)]
ggplot(counts_by_day[course %in% c("English", "French"),],
aes(x = doy_posix, y = trials_total_week, colour = course)) +
geom_line() +
scale_x_datetime(date_breaks = "3 months", date_labels = "%e %b %Y") +
scale_y_continuous(labels = number_format) +
labs(x = NULL,
y = "Number of trials per week",
colour = "Course") +
theme_paper

Overlap the two school years:
counts_by_day[, school_year := ifelse(doy_posix < "2019-08-01", "18/19", "19/20")]
counts_by_day[school_year == "18/19", doy_posix_aligned := as.POSIXct(doy_posix + 365*24*60*60, origin = "1970-01-01")]
counts_by_day[school_year == "19/20", doy_posix_aligned := doy_posix]
p_trial_hist <- ggplot(counts_by_day[course %in% c("English", "French"),],
aes(x = doy_posix_aligned, ymin = 0, ymax = trials_total_week, group = school_year, colour = school_year, fill = school_year)) +
facet_wrap(~ course, ncol = 1) +
geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -2e5, ymax = 2.2e6, fill = "grey92", colour = "grey50", lty = 2) +
geom_ribbon(alpha = .2) +
scale_x_datetime(expand = c(0, 0),
breaks = as.POSIXct(c(
"2019-09-01 02:00:00 CET",
"2019-11-01 02:00:00 CET",
"2020-01-01 02:00:00 CET",
"2020-03-01 02:00:00 CET",
"2020-05-01 02:00:00 CET",
"2020-07-01 02:00:00 CET")),
limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
date_labels = "%b") +
scale_y_continuous(expand = c(0, 0), limits = c(0, 2e6), labels = number_format) +
scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
labs(x = NULL,
y = "Trials per week",
colour = "School year",
fill = "School year") +
theme_paper
p_trial_hist

ggsave("../output/trial_hist.pdf", width = 5, height = 3)
ggsave("../output/trial_hist.eps", width = 5, height = 3)
Warning in grid.Call.graphics(C_polygon, x$x, x$y, index): semi-
transparency is not supported on this device: reported only once per page
ggsave("../output/trial_hist.png", width = 5, height = 3)
Make a line-plot version of the histogram.
p_trial_hist_line <- ggplot(counts_by_day[course %in% c("English", "French")],
aes(x = doy_posix_aligned, y = trials_total_week, group = school_year, colour = school_year, fill = school_year)) +
facet_wrap(~ course, ncol = 1) +
geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -2e5, ymax = 2.2e6, fill = "grey92", colour = "grey50", lty = 2) +
geom_line() +
scale_x_datetime(expand = c(0, 0),
breaks = as.POSIXct(c(
"2019-09-01 02:00:00 CET",
"2019-11-01 02:00:00 CET",
"2020-01-01 02:00:00 CET",
"2020-03-01 02:00:00 CET",
"2020-05-01 02:00:00 CET",
"2020-07-01 02:00:00 CET")),
limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
date_labels = "%b") +
scale_y_continuous(expand = c(0, 0), limits = c(0, 2e6), labels = number_format) +
scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
labs(x = NULL,
y = "Trials per week",
colour = "School year",
fill = "School year") +
theme_paper
p_trial_hist_line
Warning: Removed 101 row(s) containing missing values (geom_path).

ggsave("../output/trial_hist_line.pdf", width = 5, height = 3)
Warning: Removed 101 row(s) containing missing values (geom_path).
ggsave("../output/trial_hist_line.eps", width = 5, height = 3)
Warning: Removed 101 row(s) containing missing values (geom_path).
ggsave("../output/trial_hist_line.png", width = 5, height = 3)
Warning: Removed 101 row(s) containing missing values (geom_path).
Also make a difference plot.
# In order for the Mondays to align, move the 18/19 data forward by 1 year - 1 day.
counts_by_day[school_year == "18/19", doy_posix_aligned := as.POSIXct(doy_posix + 364*24*60*60, origin = "1970-01-01")]
counts_by_day[school_year == "19/20", doy_posix_aligned := doy_posix]
counts_by_day[, year_diff := trials_total_week[2] - trials_total_week[1], by = .(course, doy_posix_aligned)]
ggplot(counts_by_day[course %in% c("English", "French")],
aes(x = doy_posix_aligned, y = year_diff)) +
facet_wrap(~ course, ncol = 1) +
geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -1e6, ymax = 1.1e6, fill = "grey92", colour = "grey50", lty = 2) +
geom_hline(yintercept = 0, lty = 3) +
geom_line() +
scale_x_datetime(expand = c(0, 0),
breaks = as.POSIXct(c(
"2019-09-01 02:00:00 CET",
"2019-11-01 02:00:00 CET",
"2020-01-01 02:00:00 CET",
"2020-03-01 02:00:00 CET",
"2020-05-01 02:00:00 CET",
"2020-07-01 02:00:00 CET")),
limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
date_labels = "%b") +
scale_y_continuous(expand = c(0, 0), limits = c(-3e5, 1e6), labels = number_format) +
labs(x = NULL,
y = "Trials per week",
colour = "School year",
fill = "School year") +
theme_paper
Warning: Removed 101 row(s) containing missing values (geom_path).

Number of trials by user and week
counts[, doy_posix_week := cut.POSIXt(doy_posix, "week")]
counts_by_user_and_week <- counts[, .(trials_user = sum(trials, na.rm = TRUE)), by = .(course, school_year, user, doy_posix_week)]
Save for clustering analysis
saveRDS(na.omit(counts_by_user_and_week[course %in% c("English", "French")]), "../data/trials_by_user_and_week.rds")
Unique users by day
users_by_day <- counts[, .(unique_users = length(unique(user))), by = .(course, doy_posix)]
p <- ggplot(users_by_day, aes(x = doy_posix, y = unique_users, colour = course)) +
geom_line() +
scale_x_datetime(date_breaks = "3 months", date_labels = "%e %b %Y") +
scale_y_continuous(labels = number_format) +
labs(x = NULL,
y = "Number of users per day",
colour = "Course") +
theme_paper
p

Unique users by week
Use cut.Date() to bin dates by week. Each day is assigned the date of the most recent Monday.
users_by_day[, doy_posix_week := cut.POSIXt(doy_posix, "week")]
users_by_week <- counts[, .(unique_users_week = length(unique(user))), by = .(course, doy_posix_week)]
users_by_week <- users_by_day[users_by_week, on = .(course, doy_posix_week)]
p <- ggplot(users_by_week, aes(x = doy_posix, ymin = 0, ymax = unique_users_week, group = course, colour = course, fill = course)) +
facet_wrap(~ course, ncol = 1, scales = "free_y") +
geom_ribbon(alpha = .2) +
scale_x_datetime(date_breaks = "3 months", date_labels = "%e %b %Y") +
scale_y_continuous(labels = scales::number_format(big.mark = ".", decimal.mark = ",")) +
labs(x = NULL,
y = "Aantal gebruikers",
title = "Aantal verschillende gebruikers per week",
caption = "Let op: schaal verschilt tussen de grafieken",
colour = "Lesmethode",
fill = "Lesmethode") +
guides(colour = FALSE, fill = FALSE) +
theme_paper
p

Overlap the two school years:
users_by_week[, school_year := ifelse(doy_posix < "2019-08-01", "18/19", "19/20")]
users_by_week[school_year == "18/19", doy_posix_aligned := as.POSIXct(doy_posix + 365*24*60*60, origin = "1970-01-01")]
users_by_week[school_year == "19/20", doy_posix_aligned := doy_posix]
p_user_hist <- ggplot(users_by_week[course %in% c("English", "French"),],
aes(x = doy_posix_aligned, ymin = 0, ymax = unique_users_week, group = school_year, colour = school_year, fill = school_year)) +
facet_wrap(~ course, ncol = 1) +
geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -800, ymax = 8800, fill = "grey92", colour = "grey50", lty = 2) +
geom_ribbon(alpha = .2) +
scale_x_datetime(expand = c(0, 0),
breaks = as.POSIXct(c(
"2019-09-01 02:00:00 CET",
"2019-11-01 02:00:00 CET",
"2020-01-01 02:00:00 CET",
"2020-03-01 02:00:00 CET",
"2020-05-01 02:00:00 CET",
"2020-07-01 02:00:00 CET")),
limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
date_labels = "%b") +
scale_y_continuous(expand = c(0, 0), limits = c(0, 8000), labels = number_format) +
scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
labs(x = NULL,
y = "Unique users per week",
colour = "School year",
fill = "School year") +
theme_paper
p_user_hist

ggsave("../output/user_hist.pdf", width = 5, height = 3)
ggsave("../output/user_hist.eps", width = 5, height = 3)
Warning in grid.Call.graphics(C_polygon, x$x, x$y, index): semi-
transparency is not supported on this device: reported only once per page
ggsave("../output/user_hist.png", width = 5, height = 3)
Make a combined plot of trial and user counts for in the paper:
p_legend <- get_legend(p_trial_hist)
p_trial_hist <- p_trial_hist +
guides(colour = FALSE, fill = FALSE)
p_user_hist <- p_user_hist +
guides(colour = FALSE, fill = FALSE)
plot_grid(plot_grid(p_trial_hist, p_user_hist,
labels = c("A", "B"),
align = "v", axis = "tblr"),
p_legend,
rel_widths = c(1, .2))

ggsave("../output/combi_hist.pdf", width = 9, height = 3)
ggsave("../output/combi_hist.eps", width = 9, height = 3)
Warning in grid.Call.graphics(C_polygon, x$x, x$y, index): semi-
transparency is not supported on this device: reported only once per page
ggsave("../output/combi_hist.png", width = 9, height = 3)
Activity during the week
Get number of trials by method, day, hour, and user:
db <- db_connect()
counts_by_hour <- dbGetQuery(db,"SELECT r.method AS 'method',
DATE(r.date + 3600, 'unixepoch') AS 'doy',
STRFTIME('%H', r.date + 3600, 'unixepoch') AS 'hour',
r.user_id AS 'user',
COUNT(*) AS 'trials'
FROM 'responses_noduplicates' r
GROUP BY r.method,
DATE(r.date + 3600, 'unixepoch'),
STRFTIME('%H', r.date + 3600, 'unixepoch'),
r.user_id
")
db_disconnect(db)
setDT(counts_by_hour)
Interpolate missing days and hours:
counts_by_hour[, doy_posix := as.POSIXct(doy)]
counts_by_hour[, hour := as.numeric(hour)]
doy_posix <- seq.POSIXt(from = counts_by_hour[,min(doy_posix)], to = counts_by_hour[,max(doy_posix)], by = "DSTday")
method <- counts_by_hour[,unique(method)]
hour <- 0:23
dates_and_hours <- CJ(doy_posix, hour, method)
counts_by_hour <- merge(counts_by_hour, dates_and_hours, by = c("doy_posix", "hour", "method"), all = TRUE)
Add day of the week:
counts_by_hour[, weekday := weekdays(doy_posix)]
Distinguish between school years:
counts_by_hour[, school_year := ifelse(doy_posix < "2019-08-01", "18/19", "19/20")]
Add quarter:
counts_by_hour[, quarter := paste0(year(doy_posix), "Q", quarter(doy_posix))]
Add exact school closure period in both school years:
counts_by_hour[school_year == "18/19", doy_posix_aligned := as.POSIXct(doy_posix + 365*24*60*60, origin = "1970-01-01")]
counts_by_hour[school_year == "19/20", doy_posix_aligned := doy_posix]
counts_by_hour[, schools_closed := doy_posix_aligned >= date_schools_closed & doy_posix_aligned < date_schools_opened]
Add more sensible course names:
counts_by_hour[, course := ifelse(method == "Grandes Lignes", "French", ifelse(method == "Stepping Stones", "English", "German"))]
Sum trials by school year, weekday and hour:
counts_by_hour[, trials_schoolyear := sum(trials, na.rm = TRUE), by = .(course, school_year, weekday, hour)]
Also sum trials by quarter, weekday and hour:
counts_by_hour[, trials_quarter := sum(trials, na.rm = TRUE), by = .(course, quarter, weekday, hour)]
And sum trials within the closure period by weekday and hour:
counts_by_hour[schools_closed == TRUE, trials_closed := sum(trials, na.rm = TRUE), by = .(course, school_year, weekday, hour)]
trials_by_wday_hour <- unique(counts_by_hour, by = c("course", "school_year", "quarter", "schools_closed", "weekday", "hour"))
trials_by_wday_hour[, trials_normalised_schoolyear := trials_schoolyear / sum(trials_schoolyear), by = .(course)]
trials_by_wday_hour[, trials_normalised_quarter := trials_quarter / sum(trials_quarter), by = .(course)]
trials_by_wday_hour[, weekday := ordered(weekday, levels = c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"))]
# trials_by_wday_hour[, weekday := ordered(weekday, levels = c("maandag", "dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag", "zondag"))]
Plot heatmap for the whole school year:
ggplot(trials_by_wday_hour[course %in% c("English", "French")],
aes(x = hour, y = reorder(weekday, dplyr::desc(weekday)), fill = trials_normalised_schoolyear)) +
facet_grid(school_year ~ course) +
geom_tile(colour = "white", size = 0.25) +
labs(x = "Time of day (hour)",
y = NULL) +
scale_x_continuous(expand = c(0,0), breaks = seq(0, 24, 3)) +
scale_y_discrete(expand = c(0,0)) +
scale_fill_viridis_c(option = "A", direction = -1) +
coord_fixed() +
guides(fill = FALSE) +
theme_paper

Plot heatmap per quarter:
ggplot(trials_by_wday_hour, aes(x = hour, y = reorder(weekday, dplyr::desc(weekday)), fill = trials_normalised_quarter)) +
facet_grid(quarter ~ method) +
geom_tile(colour = "white", size = 0.25) +
labs(x = NULL,
y = NULL,
title = "Activiteit per uur gedurende de week",
caption = "Aantal trials per weekdag en uur in elk kwartaal, genormaliseerd per methode.") +
scale_x_continuous(expand = c(0,0), breaks = seq(0, 24, 3)) +
scale_y_discrete(expand = c(0,0)) +
scale_fill_viridis_c(option = "A", direction = -1) +
coord_fixed() +
guides(fill = FALSE) +
theme_bw(base_size = 16)

Plot heatmap for the period in which schools were closed:
trials_closed <- unique(trials_by_wday_hour[schools_closed == TRUE, .(course, school_year, weekday, hour, trials_closed)])
trials_closed[, trials_normalised_closed := trials_closed / sum(trials_closed), by = .(course, school_year)]
trials_closed_diff <- trials_closed[, .(school_year = "Change",
trials_closed = trials_closed[school_year == "19/20"] - trials_closed[school_year == "18/19"],
trials_normalised_closed = trials_normalised_closed[school_year == "19/20"] - trials_normalised_closed[school_year == "18/19"]), by = .(course, weekday, hour)]
p_heatmap <- ggplot(trials_closed[course %in% c("English", "French"),],
aes(x = hour, y = reorder(weekday, dplyr::desc(weekday)), fill = trials_normalised_closed)) +
facet_grid(school_year ~ course) +
geom_tile(colour = "white", size = 0.25) +
labs(x = "Time of day (hour)",
y = NULL,
fill = NULL) +
scale_x_continuous(expand = c(0,0), breaks = seq(0, 24, 3)) +
scale_y_discrete(expand = c(0,0)) +
scale_fill_viridis_c(option = "A", direction = -1) +
coord_fixed() +
theme_paper
p_heatmap

Make a plot of the difference between the two school years during the school closure period:
p_heatmap_diff <- ggplot(trials_closed_diff[course %in% c("English", "French"),],
aes(x = hour, y = reorder(weekday, dplyr::desc(weekday)), fill = trials_normalised_closed)) +
facet_grid(school_year ~ course) +
geom_tile(colour = "white", size = 0.25) +
labs(x = "Time of day (hour)",
y = NULL,
fill = NULL) +
scale_x_continuous(expand = c(0,0), breaks = seq(0, 24, 3)) +
scale_y_discrete(expand = c(0,0)) +
scale_fill_distiller(type = "div", palette = "RdBu", direction = -1, limits = c(-1, 1) * max(abs(trials_closed_diff[course %in% c("English", "French"),]$trials_normalised_closed))) +
coord_fixed() +
theme_paper
p_heatmap_diff

Make a combined plot for in the paper:
p_heatmap_legend <- get_legend(p_heatmap)
p_heatmap_diff_legend <- get_legend(p_heatmap_diff)
p_heatmap <- p_heatmap + guides(fill = FALSE)
p_heatmap_diff <- p_heatmap_diff + guides(fill = FALSE)
plot_grid(
plot_grid(p_heatmap, p_heatmap_diff,
ncol = 1,
labels = c("A", "B"),
rel_heights = c(1, .655)
),
plot_grid(p_heatmap_legend, p_heatmap_diff_legend,
ncol = 1,
align = "vh", axis = "lrtb"),
ncol = 2,
rel_widths = c(1, .15))

ggsave("../output/combi_heatmap.pdf", width = 9, height = 4.5)
ggsave("../output/combi_heatmap.eps", width = 9, height = 4.5)
ggsave("../output/combi_heatmap.png", width = 9, height = 4.5)
Activity stratified by year and level
db <- db_connect()
counts_strat <- dbGetQuery(db,"SELECT r.method AS 'method',
r.book_info_id as 'book_info_id',
DATE(r.date + 3600, 'unixepoch') AS 'doy',
r.user_id AS 'user',
COUNT(*) AS 'trials'
FROM 'responses_noduplicates' r
GROUP BY r.method,
r.book_info_id,
DATE(r.date + 3600, 'unixepoch'),
r.user_id
")
db_disconnect(db)
setDT(counts_strat)
db <- db_connect()
book_info <- dbGetQuery(db, "SELECT * FROM 'book_info'")
db_disconnect(db)
setDT(book_info)
Add book information:
counts_strat[book_info, on = "book_info_id", c("book_title", "method_group") := .(i.book_title, i.method_group)]
Add a school year column (cutoff date: 1 August):
counts_strat[, doy_posix := as.POSIXct(doy)]
counts_strat[, school_year := ifelse(doy_posix < "2019-08-01", "18/19", "19/20")]
Add sensible course names:
counts_strat[, course := ifelse(method == "Grandes Lignes", "French", ifelse(method == "Stepping Stones", "English", "German"))]
Count trials by day:
counts_strat_by_day <- counts_strat[, .(trials_total = sum(trials, na.rm = TRUE)), by = .(school_year, course, method_group, book_title, doy_posix)]
setorder(counts_strat_by_day, school_year, course, method_group, book_title, doy_posix)
Simplify level names:
# Keep all distinctions
counts_strat_by_day[, book_title_simple := stringr::str_sub(book_title, 3, -10)]
counts_strat_by_day[, book_title_simple := factor(book_title_simple, levels = c("vmbo b/lwoo", "vmbo b", "vmbo bk", "vmbo k", "vmbo kgt", "vmbo-gt", "vmbo gt", "vmbo-gt/havo", "vmbo (t)hv", "havo", "havo vwo", "vwo"))]
# Simplify to three levels
counts_strat_by_day[, level := dplyr::case_when(
grepl( "hv", book_title) ~ "General secondary\n(havo)",
grepl("vmbo", book_title) ~ "Pre-vocational\n(vmbo)",
grepl("havo", book_title) ~ "General secondary\n(havo)",
grepl("vwo", book_title) ~ "Pre-university\n(vwo)",
TRUE ~ "Other")]
counts_strat_by_day[, level := factor(level, levels = c("Other", "Pre-vocational\n(vmbo)", "General secondary\n(havo)", "Pre-university\n(vwo)"))]
Simplify year names:
counts_strat_by_day[, year := dplyr::case_when(
method_group == "Leerjaar 1 (5e Ed.)" ~ "Year 1",
method_group == "Leerjaar 2 (5e Ed.)" ~ "Year 2",
method_group == "Leerjaar 3 (5e Ed.)" ~ "Year 3",
method_group == "Leerjaar 3/4 (5e Ed.)" ~ "Year 3/4",
method_group == "Leerjaar 4 (5e Ed.)" ~ "Year 4",
method_group == "Tweede Fase (6e Ed.)" ~ "Tweede Fase",
TRUE ~ "Other")]
Align school years:
counts_strat_by_day[school_year == "18/19", doy_posix_aligned := as.POSIXct(doy_posix + 365*24*60*60, origin = "1970-01-01")]
counts_strat_by_day[school_year == "19/20", doy_posix_aligned := doy_posix]
Use cut.Date() to bin dates by week. Each day is assigned the date of the most recent Monday.
counts_strat_by_day[, doy_posix_aligned_week := cut.POSIXt(doy_posix_aligned, "week")]
counts_strat_by_day[, trials_total_week := sum(trials_total, na.rm = TRUE), by = .(school_year, course, method_group, book_title_simple, doy_posix_aligned_week)]
counts_strat_by_day[, trials_total_week_level := sum(trials_total), by = .(school_year, course, method_group, level, doy_posix_aligned_week)]
Summarise increase during lockdown:
counts_strat_increase <- counts_strat_by_day[between(doy_posix_aligned, date_schools_closed, date_schools_opened), .(trials_lockdown = sum(trials_total)), by = .(course, book_title_simple, method_group, year, school_year)]
counts_strat_increase[, increase := trials_lockdown[2]/trials_lockdown[1], by = .(course, book_title_simple, method_group, year)]
counts_strat_increase[, increase_pct := paste0("Change:\n", scales::percent(increase, accuracy = 2))]
counts_strat_increase_level <- counts_strat_by_day[between(doy_posix_aligned, date_schools_closed, date_schools_opened), .(trials_lockdown = sum(trials_total)), by = .(course, level, method_group, year, school_year)]
counts_strat_increase_level[, increase := trials_lockdown[2]/trials_lockdown[1], by = .(course, level, method_group, year)]
counts_strat_increase_level[, increase_pct := paste0("Change:\n", scales::percent(increase, accuracy = 2))]
French
ggplot(counts_strat_by_day[course == "French"],
aes(group = school_year, colour = school_year, fill = school_year)) +
facet_grid(book_title_simple ~ method_group) +
geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -2e5, ymax = 2.2e6, fill = "grey92", colour = "grey50", lty = 2) +
geom_ribbon(aes(x = doy_posix_aligned, ymin = 0, ymax = trials_total_week, ), alpha = .2) +
geom_text(data = counts_strat_increase[course == "French" & school_year == "19/20"],
aes(label = increase_pct),
x = as.POSIXct((as.numeric(date_schools_closed) + as.numeric(date_schools_opened))/2, origin = "1970-01-01"),
y = 3.6e5,
colour = "black",
vjust = 1,
show.legend = FALSE) +
scale_x_datetime(expand = c(0, 0),
breaks = as.POSIXct(c(
"2019-09-01 02:00:00 CET",
"2019-11-01 02:00:00 CET",
"2020-01-01 02:00:00 CET",
"2020-03-01 02:00:00 CET",
"2020-05-01 02:00:00 CET",
"2020-07-01 02:00:00 CET")),
limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
date_labels = "%b") +
scale_y_continuous(expand = c(0, 0), limits = c(0, 3.75e5), labels = number_format) +
scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
labs(x = NULL,
y = "Trials per week",
colour = "School year",
fill = "School year",
title = "French") +
theme_paper

ggsave("../output/trial_hist_french.pdf", width = 14, height = 10)
ggsave("../output/trial_hist_french.eps", width = 14, height = 10)
Warning in grid.Call.graphics(C_polygon, x$x, x$y, index): semi-
transparency is not supported on this device: reported only once per page
ggsave("../output/trial_hist_french.png", width = 14, height = 10)
Streamlined version for in the paper:
ggplot(counts_strat_by_day[course == "French"],
aes(group = school_year, colour = school_year, fill = school_year)) +
facet_grid(level ~ year) +
geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -2e5, ymax = 2.2e6, fill = "grey92", colour = "grey50", lty = 2) +
geom_ribbon(aes(x = doy_posix_aligned, ymin = 0, ymax = trials_total_week_level, ), alpha = .2) +
geom_text(data = counts_strat_increase_level[course == "French" & school_year == "19/20"],
aes(label = increase_pct),
x = as.POSIXct((as.numeric(date_schools_closed) + as.numeric(date_schools_opened))/2, origin = "1970-01-01"),
y = 3.6e5,
colour = "black",
vjust = 1,
size = rel(2.75),
show.legend = FALSE) +
scale_x_datetime(expand = c(0, 0),
breaks = as.POSIXct(c(
"2019-10-01 02:00:00 CET",
"2019-12-01 02:00:00 CET",
"2020-02-01 02:00:00 CET",
"2020-04-01 02:00:00 CET",
"2020-06-01 02:00:00 CET")),
limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
date_labels = "%b") +
scale_y_continuous(expand = c(0, 0), limits = c(0, 3.75e5), labels = number_format) +
scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
labs(x = NULL,
y = "Trials per week",
colour = "School year",
fill = "School year") +
theme_paper

ggsave("../output/trial_hist_french_level.pdf", width = 9, height = 5)
ggsave("../output/trial_hist_french_level.eps", width = 9, height = 5)
Warning in grid.Call.graphics(C_polygon, x$x, x$y, index): semi-
transparency is not supported on this device: reported only once per page
ggsave("../output/trial_hist_french_level.png", width = 9, height = 5)
There are two reasons why total trial count may increase: there are more active users, or active users complete more trials. Given the fixed curriculum, the first reason seems more plausible. We can confirm this by plotting the weekly number of trials per user.
users_strat <- copy(counts_strat)
users_strat[, book_title_simple := stringr::str_sub(book_title, 3, -10)]
users_strat[, book_title_simple := factor(book_title_simple, levels = c("vmbo b/lwoo", "vmbo b", "vmbo bk", "vmbo k", "vmbo kgt", "vmbo-gt", "vmbo gt", "vmbo-gt/havo", "vmbo (t)hv", "havo", "havo vwo", "vwo"))]
# Simplify to three levels
users_strat[, level := dplyr::case_when(
grepl( "hv", book_title) ~ "General secondary\n(havo)",
grepl("vmbo", book_title) ~ "Pre-vocational\n(vmbo)",
grepl("havo", book_title) ~ "General secondary\n(havo)",
grepl("vwo", book_title) ~ "Pre-university\n(vwo)",
TRUE ~ "Other")]
users_strat[, level := factor(level, levels = c("Other", "Pre-vocational\n(vmbo)", "General secondary\n(havo)", "Pre-university\n(vwo)"))]
users_strat[, year := dplyr::case_when(
method_group == "Leerjaar 1 (5e Ed.)" ~ "Year 1",
method_group == "Leerjaar 2 (5e Ed.)" ~ "Year 2",
method_group == "Leerjaar 3 (5e Ed.)" ~ "Year 3",
method_group == "Leerjaar 3/4 (5e Ed.)" ~ "Year 3/4",
method_group == "Leerjaar 4 (5e Ed.)" ~ "Year 4",
method_group == "Tweede Fase (6e Ed.)" ~ "Tweede Fase",
TRUE ~ "Other")]
users_strat[school_year == "18/19", doy_posix_aligned := as.POSIXct(doy_posix + 365*24*60*60, origin = "1970-01-01")]
users_strat[school_year == "19/20", doy_posix_aligned := doy_posix]
users_strat[, doy_posix_aligned_week := cut.POSIXt(doy_posix_aligned, "week")]
users_strat_by_week <- users_strat[, .(users_week_level = .N), by = .(school_year, course, year, level, doy_posix_aligned_week)]
counts_strat_by_day <- counts_strat_by_day[users_strat_by_week, on = c("school_year", "course", "year", "level", "doy_posix_aligned_week")][, trials_per_user_week := trials_total_week_level / users_week_level]
ggplot(counts_strat_by_day[course == "French"],
aes(group = school_year, colour = school_year, fill = school_year)) +
facet_grid(level ~ year) +
geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -2e5, ymax = 2.2e6, fill = "grey92", colour = "grey50", lty = 2) +
geom_ribbon(aes(x = doy_posix_aligned, ymin = 0, ymax = trials_per_user_week, ), alpha = .2) +
scale_x_datetime(expand = c(0, 0),
breaks = as.POSIXct(c(
"2019-10-01 02:00:00 CET",
"2019-12-01 02:00:00 CET",
"2020-02-01 02:00:00 CET",
"2020-04-01 02:00:00 CET",
"2020-06-01 02:00:00 CET")),
limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
date_labels = "%b") +
scale_y_continuous(expand = c(0, 0), labels = number_format) +
scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
labs(x = NULL,
y = "Trials per user per week",
colour = "School year",
fill = "School year") +
theme_paper

As expected, there is essentially no change in the number of trials each user completes, so the increased trial count that we see comes from more users being active.
English
ggplot(counts_strat_by_day[course == "English"],
aes(group = school_year, colour = school_year, fill = school_year)) +
facet_grid(book_title_simple ~ method_group) +
geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -2e5, ymax = 2.2e6, fill = "grey92", colour = "grey50", lty = 2) +
geom_ribbon(aes(x = doy_posix_aligned, ymin = 0, ymax = trials_total_week, ), alpha = .2) +
geom_text(data = counts_strat_increase[course == "English" & school_year == "19/20"],
aes(label = increase_pct),
x = as.POSIXct((as.numeric(date_schools_closed) + as.numeric(date_schools_opened))/2, origin = "1970-01-01"),
y = 3.6e5,
colour = "black",
vjust = 1,
show.legend = FALSE) +
scale_x_datetime(expand = c(0, 0),
breaks = as.POSIXct(c(
"2019-09-01 02:00:00 CET",
"2019-11-01 02:00:00 CET",
"2020-01-01 02:00:00 CET",
"2020-03-01 02:00:00 CET",
"2020-05-01 02:00:00 CET",
"2020-07-01 02:00:00 CET")),
limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
date_labels = "%b") +
scale_y_continuous(expand = c(0, 0), limits = c(0, 3.75e5), labels = number_format) +
scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
labs(x = NULL,
y = "Trials per week",
colour = "School year",
fill = "School year",
title = "English") +
theme_paper

ggsave("../output/trial_hist_english.pdf", width = 14, height = 10)
ggsave("../output/trial_hist_english.eps", width = 14, height = 10)
Warning in grid.Call.graphics(C_polygon, x$x, x$y, index): semi-
transparency is not supported on this device: reported only once per page
ggsave("../output/trial_hist_english.png", width = 14, height = 10)
Streamlined version for in the paper:
ggplot(counts_strat_by_day[course == "English" & level != "Other"],
aes(group = school_year, colour = school_year, fill = school_year)) +
facet_grid(level ~ year) +
geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -2e5, ymax = 2.2e6, fill = "grey92", colour = "grey50", lty = 2) +
geom_ribbon(aes(x = doy_posix_aligned, ymin = 0, ymax = trials_total_week_level, ), alpha = .2) +
geom_text(data = counts_strat_increase_level[course == "English" & level != "Other" & school_year == "19/20"],
aes(label = increase_pct),
x = as.POSIXct((as.numeric(date_schools_closed) + as.numeric(date_schools_opened))/2, origin = "1970-01-01"),
y = 9.6e5,
colour = "black",
vjust = 1,
size = rel(2.75),
show.legend = FALSE) +
scale_x_datetime(expand = c(0, 0),
breaks = as.POSIXct(c(
"2019-10-01 02:00:00 CET",
"2019-12-01 02:00:00 CET",
"2020-02-01 02:00:00 CET",
"2020-04-01 02:00:00 CET",
"2020-06-01 02:00:00 CET")),
limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
date_labels = "%b") +
scale_y_continuous(expand = c(0, 0), limits = c(0, 1e6), labels = number_format) +
scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
labs(x = NULL,
y = "Trials per week",
colour = "School year",
fill = "School year") +
theme_paper

ggsave("../output/trial_hist_english_level.pdf", width = 9, height = 5)
ggsave("../output/trial_hist_english_level.eps", width = 9, height = 5)
Warning in grid.Call.graphics(C_polygon, x$x, x$y, index): semi-
transparency is not supported on this device: reported only once per page
ggsave("../output/trial_hist_english_level.png", width = 9, height = 5)
Also plot the weekly number of trials per user. As with French, this number stays more or less constant:
ggplot(counts_strat_by_day[course == "English"],
aes(group = school_year, colour = school_year, fill = school_year)) +
facet_grid(level ~ year) +
geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -2e5, ymax = 2.2e6, fill = "grey92", colour = "grey50", lty = 2) +
geom_ribbon(aes(x = doy_posix_aligned, ymin = 0, ymax = trials_per_user_week, ), alpha = .2) +
scale_x_datetime(expand = c(0, 0),
breaks = as.POSIXct(c(
"2019-10-01 02:00:00 CET",
"2019-12-01 02:00:00 CET",
"2020-02-01 02:00:00 CET",
"2020-04-01 02:00:00 CET",
"2020-06-01 02:00:00 CET")),
limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
date_labels = "%b") +
scale_y_continuous(expand = c(0, 0), labels = number_format) +
scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
labs(x = NULL,
y = "Trials per user per week",
colour = "School year",
fill = "School year") +
theme_paper

Question type
There are different question formats: open-answer, in which the student types the answer, and multiple-choice, in which the student selects the answer from a set of 3 or 4 options.
db <- db_connect()
question_type <- dbGetQuery(db,
"SELECT r.method AS 'method',
DATE(r.date + 3600, 'unixepoch') AS 'doy',
r.choices AS 'choices',
COUNT(*) AS 'n'
FROM 'responses_noduplicates' r
WHERE r.study == 0
GROUP BY r.method,
DATE(r.date + 3600, 'unixepoch'),
r.choices"
)
setDT(question_type)
db_disconnect(db)
Add a school year column (cutoff date: 1 August):
question_type[, doy_posix := as.POSIXct(doy)]
question_type[, school_year := ifelse(doy_posix < "2019-08-01", "18/19", "19/20")]
Add sensible course names:
question_type[, course := ifelse(method == "Grandes Lignes", "French", ifelse(method == "Stepping Stones", "English", "German"))]
Align school years:
question_type[school_year == "18/19", doy_posix_aligned := as.POSIXct(doy_posix + 365*24*60*60, origin = "1970-01-01")]
question_type[school_year == "19/20", doy_posix_aligned := doy_posix]
Use cut.Date() to bin dates by week. Each day is assigned the date of the most recent Monday.
question_type[, doy_posix_week := cut.POSIXt(doy_posix, "week")]
question_type[, doy_posix_aligned_week := cut.POSIXt(doy_posix_aligned, "week")]
question_type_by_week <- question_type[, .(n = sum(n)), by = .(course, school_year, doy_posix_aligned_week, choices)]
ggplot(question_type_by_week[course %in% c("English", "French")], aes(x = as.POSIXct(doy_posix_aligned_week), y = n, group = interaction(school_year,as.factor(choices)), colour = school_year)) +
facet_grid(course ~ choices) +
geom_line() +
scale_x_datetime(expand = c(0, 0),
breaks = as.POSIXct(c(
"2019-10-01 02:00:00 CET",
"2019-12-01 02:00:00 CET",
"2020-02-01 02:00:00 CET",
"2020-04-01 02:00:00 CET",
"2020-06-01 02:00:00 CET")),
limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
date_labels = "%b") +
labs(x = NULL,
y = "Trials",
colour = "School year") +
theme_paper
Warning: Removed 24 row(s) containing missing values (geom_path).

question_type[, .(n = sum(n)), by = .(course, mcq = choices>1, school_year)][, .(perc_mcq = n[mcq == TRUE]/sum(n)), by = .(course, school_year)]
There is a clear difference between the languages in the question format used: English uses almost exclusively 4-alternative MCQs, while French uses a mix of MCQs (including a small number of 3-alternative questions) and open-answer questions.
LS0tCnRpdGxlOiAnU2xpbVN0YW1wZW4gVXNhZ2UgRHVyaW5nIExvY2tkb3duJwphdXRob3I6ICJNYWFydGVuIHZhbiBkZXIgVmVsZGUiCmRhdGU6ICJMYXN0IHVwZGF0ZWQ6IGByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgZ2l0aHViX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICBodG1sX25vdGVib29rOgogICAgc21hcnQ6IG5vCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgojIFNldHVwCgpgYGB7cn0KbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KERCSSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkoZ3JpZCkKClN5cy5zZXRsb2NhbGUoIkxDX1RJTUUiLCAiZW5fVVMuVVRGLTgiKSAjIFByaW50IEVuZ2xpc2ggZGF0ZSBmb3JtYXQKIyBTeXMuc2V0bG9jYWxlKCJMQ19USU1FIiwgIm5sX05MLlVURi04IikgIyBQcmludCBEdXRjaCBkYXRlIGZvcm1hdAoKbnVtYmVyX2Zvcm1hdCA8LSBzY2FsZXM6Om51bWJlcl9mb3JtYXQoYmlnLm1hcmsgPSAiLCIsIGRlY2ltYWwubWFyayA9ICIuIikgIyBQcmludCBFbmdsaXNoIG51bWJlciBmb3JtYXQKIyBudW1iZXJfZm9ybWF0IDwtIHNjYWxlczo6bnVtYmVyX2Zvcm1hdChiaWcubWFyayA9ICIuIiwgZGVjaW1hbC5tYXJrID0gIiwiKSAjIFByaW50IER1dGNoIG51bWJlciBmb3JtYXQKCnRoZW1lX3BhcGVyIDwtIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTIpICsgCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJibGFjayIpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiZ3JleTkyIikpCmBgYAoKU2Nob29sIGNsb3N1cmUgYW5kIG9wZW5pbmcgZGF0ZXMKClNvdXJjZXM6CgogIC0gaHR0cHM6Ly93d3cucmlqa3NvdmVyaGVpZC5ubC9hY3R1ZWVsL25pZXV3cy8yMDIwLzAzLzE1L2FhbnZ1bGxlbmRlLW1hYXRyZWdlbGVuLW9uZGVyd2lqcy1ob3JlY2Etc3BvcnQKICAtIGh0dHBzOi8vd3d3LnJpamtzb3ZlcmhlaWQubmwvYWN0dWVlbC9uaWV1d3MvMjAyMC8wNS8xOS9vbmRlcndpanMtZ2FhdC1zdGFwLXZvb3Itc3RhcC1vcGVuCgpgYGB7cn0KZGF0ZV9zY2hvb2xzX2Nsb3NlZCA8LSBhcy5QT1NJWGN0KCIyMDIwLTAzLTE2IikKZGF0ZV9zY2hvb2xzX29wZW5lZCA8LSBhcy5QT1NJWGN0KCIyMDIwLTA2LTAyIikKYGBgCgoKSGFuZGxlIGRhdGFiYXNlIGNvbm5lY3Rpb25zCmBgYHtyfQpkYl9jb25uZWN0IDwtIGZ1bmN0aW9uKCkgewogIGRiIDwtIGRiQ29ubmVjdChSU1FMaXRlOjpTUUxpdGUoKSwgZmlsZS5wYXRoKCIuLiIsICJkYXRhIiwgIm5vb3JkaG9mZi5zcWxpdGUiKSkKICByZXR1cm4oZGIpCn0KCmRiX2Rpc2Nvbm5lY3QgPC0gZnVuY3Rpb24oZGIpIHsKICBkYkRpc2Nvbm5lY3QoZGIpCn0KYGBgCgoKIyBEYXRhCgpUaGUgZGF0YWJhc2UgY29udGFpbnMgYWxsIFNsaW1TdGFtcGVuIGRhdGEgY29sbGVjdGVkIHZpYSBOb29yZGhvZmYncyBwbGF0Zm9ybSBpbiB0aHJlZSBjb3Vyc2VzOiAqU3RlcHBpbmcgU3RvbmVzKiAoRW5nbGlzaCksICpHcmFuZGVzIExpZ25lcyogKEZyZW5jaCksIGFuZCAqTmV1ZSBLb250YWt0ZSogKEdlcm1hbikuCgpUcmlhbC1sZXZlbCByZXNwb25zZSBkYXRhIGFyZSBzdG9yZWQgaW4gdGhlIGByZXNwb25zZXNgIHRhYmxlLgpCb29rIGluZm9ybWF0aW9uLCBzdWNoIGFzIHRoZSBjb3Vyc2UgeWVhciwgYm9vayB0aXRsZSwgYW5kIGNoYXB0ZXIsIGFyZSBzdG9yZWQgaW4gdGhlIGBib29rX2luZm9gIHRhYmxlLgoKIyMgYHJlc3BvbnNlc2AKCnwgQ29sdW1uICAgICAgICAgICAgICAgfCBUeXBlICAgICAgfCBFeHBsYW5hdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwKfCBkYXRlICAgICAgICAgICAgICAgICB8IGludCAgICAgICB8IFVOSVggdGltZSBzdGFtcCBbc10gICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgdXNlcl9pZCAgICAgICAgICAgICAgfCBjaHIgICAgICAgfCB1bmlxdWUgdXNlciBpZGVudGlmaWVyICAgICAgICAgICAgICAgICAgICAgICAgfAp8IG1ldGhvZCAgICAgICAgICAgICAgIHwgY2hyICAgICAgIHwgY291cnNlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBzdGFydF90aW1lICAgICAgICAgICB8IGludCAgICAgICB8IGVsYXBzZWQgdGltZSBzaW5jZSBzZXNzaW9uIHN0YXJ0IFttc10gICAgICAgICB8CnwgcnQgICAgICAgICAgICAgICAgICAgfCBpbnQgICAgICAgfCByZXNwb25zZSB0aW1lIFttc10gICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGR1cmF0aW9uICAgICAgICAgICAgIHwgaW50ICAgICAgIHwgdHJpYWwgZHVyYXRpb24gW21zXSAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBmYWN0X2lkICAgICAgICAgICAgICB8IGludCAgICAgICB8IHVuaXF1ZSBmYWN0IGlkZW50aWZpZXIgKHdpdGhpbiBjaGFwdGVyKSAgICAgICB8CnwgY29ycmVjdCAgICAgICAgICAgICAgfCBpbnQgICAgICAgfCByZXNwb25zZSBhY2N1cmFjeSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGFuc3dlciAgICAgICAgICAgICAgIHwgY2hyICAgICAgIHwgdXNlcidzIHJlc3BvbnNlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBjaG9pY2VzICAgICAgICAgICAgICB8IGludCAgICAgICB8IG51bWJlciBvZiBhbnN3ZXIgY2hvaWNlcyAoMSA9PSBvcGVuIHJlc3BvbnNlKSB8CnwgYmFja3NwYWNlX3VzZWQgICAgICAgfCBkYmwgICAgICAgfCB1c2VyIHByZXNzZWQgYmFja3NwYWNlIGR1cmluZyB0cmlhbCAgICAgICAgICAgfAp8IGJhY2tzcGFjZV91c2VkX2ZpcnN0IHwgZGJsICAgICAgIHwgdXNlciBlcmFzZWQgZmlyc3QgY2hhcmFjdGVyIG9mIHJlc3BvbnNlICAgICAgIHwKfCBzdHVkeSAgICAgICAgICAgICAgICB8IGludCAgICAgICB8IHRyaWFsIHdhcyBhIHN0dWR5IHRyaWFsICAgICAgICAgICAgICAgICAgICAgICB8CnwgYW5zd2VyX2xhbmd1YWdlICAgICAgfCBjaHIgICAgICAgfCBsYW5ndWFnZSBvZiB0aGUgYW5zd2VyICAgICAgICAgICAgICAgICAgICAgICAgfAp8IHN1YnNlc3Npb24gICAgICAgICAgIHwgaW50ICAgICAgIHwgaWRlbnRpZmllcyBwYXJ0IHdpdGhpbiBsZWFybmluZyBzZXNzaW9uICAgICAgIHwKfCBib29rX2luZm9faWQgICAgICAgICB8IGNociAgICAgICB8IHVuaXF1ZSBpZGVudGlmaWVyIG9mIGJvb2sgaW5mb3JtYXRpb24gICAgICAgICB8CgoKIyMgYGJvb2tfaW5mb2AKCnwgQ29sdW1uICAgICAgICAgICAgICAgfCBUeXBlICAgICAgfCBFeHBsYW5hdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwKfCBib29rX2luZm9faWQgICAgICAgICB8IGNociAgICAgICB8IHVuaXF1ZSBpZGVudGlmaWVyIG9mIGJvb2sgaW5mb3JtYXRpb24gICAgICAgICB8CnwgbWV0aG9kX2dyb3VwICAgICAgICAgfCBjaHIgICAgICAgfCB5ZWFyIGFuZCBlZGl0aW9uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGJvb2tfdGl0bGUgICAgICAgICAgIHwgY2hyICAgICAgIHwgYm9vayB0aXRsZSAoaW5jbC4geWVhciwgbGV2ZWwsIGVkaXRpb24pICAgICAgIHwKfCBib29rX3R5cGUgICAgICAgICAgICB8IGNociAgICAgICB8IHR5cGUgb2YgYm9vayAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgY2hhcHRlciAgICAgICAgICAgICAgfCBjaHIgICAgICAgfCBjaGFwdGVyIG51bWJlciBhbmQgdGl0bGUgICAgICAgICAgICAgICAgICAgICAgfAoKClByZXZpZXcgZmlyc3QgMTAgcm93cwpgYGB7cn0KZGIgPC0gZGJfY29ubmVjdCgpCnJlc3BvbnNlc190b3AgPC0gZGJHZXRRdWVyeShkYiwgIlNFTEVDVCAqIEZST00gcmVzcG9uc2VzX25vZHVwbGljYXRlcyBMSU1JVCAxMCIpCnJlc3BvbnNlc190b3AKCmJvb2tfaW5mb190b3AgPC0gZGJHZXRRdWVyeShkYiwgIlNFTEVDVCAqIEZST00gYm9va19pbmZvIExJTUlUIDEwIikKYm9va19pbmZvX3RvcApkYl9kaXNjb25uZWN0KGRiKQpgYGAKCgoKIyBVc2FnZQoKR2V0IG51bWJlciBvZiB0cmlhbHMgYnkgbWV0aG9kLCBkYXksIGFuZCB1c2VyOgpgYGB7cn0KZGIgPC0gZGJfY29ubmVjdCgpCmNvdW50cyA8LSBkYkdldFF1ZXJ5KGRiLCJTRUxFQ1Qgci5tZXRob2QgQVMgJ21ldGhvZCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgREFURShyLmRhdGUgKyAzNjAwLCAndW5peGVwb2NoJykgQVMgJ2RveScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgci51c2VyX2lkIEFTICd1c2VyJywKICAgICAgICAgICAgICAgICAgICAgICAgICBDT1VOVCgqKSBBUyAndHJpYWxzJwogICAgICAgICAgICAgICAgICAgICAgICAgIEZST00gJ3Jlc3BvbnNlc19ub2R1cGxpY2F0ZXMnIHIKICAgICAgICAgICAgICAgICAgICAgICAgICBHUk9VUCBCWSByLm1ldGhvZCwKICAgICAgICAgICAgICAgICAgICAgICAgICBEQVRFKHIuZGF0ZSAgKyAzNjAwLCAndW5peGVwb2NoJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgci51c2VyX2lkCiAgICAgICAgICAgICAgICAgICAgICAgICIpCmRiX2Rpc2Nvbm5lY3QoZGIpCgpzZXREVChjb3VudHMpCmBgYAoKQWRkIGEgc2Nob29sIHllYXIgY29sdW1uIChjdXRvZmYgZGF0ZTogMSBBdWd1c3QpOgpgYGB7cn0KY291bnRzWywgZG95X3Bvc2l4IDo9IGFzLlBPU0lYY3QoZG95KV0KY291bnRzWywgc2Nob29sX3llYXIgOj0gaWZlbHNlKGRveV9wb3NpeCA8ICIyMDE5LTA4LTAxIiwgIjE4LzE5IiwgIjE5LzIwIildCmBgYAoKQWRkIG1vcmUgc2Vuc2libGUgY291cnNlIG5hbWVzOgpgYGB7cn0KY291bnRzWywgY291cnNlIDo9IGlmZWxzZShtZXRob2QgPT0gIkdyYW5kZXMgTGlnbmVzIiwgIkZyZW5jaCIsIGlmZWxzZShtZXRob2QgPT0gIlN0ZXBwaW5nIFN0b25lcyIsICJFbmdsaXNoIiwgIkdlcm1hbiIpKV0KYGBgCgoKIyMgVG90YWwgbnVtYmVyIG9mIHVuaXF1ZSB1c2VycyBieSBjb3Vyc2UgYW5kIHNjaG9vbCB5ZWFyCmBgYHtyfQpjb3VudHNbLCAuKHVuaXF1ZV91c2VycyA9IGxlbmd0aCh1bmlxdWUodXNlcikpKSwgYnkgPSAuKGNvdXJzZSwgc2Nob29sX3llYXIpXQpgYGAKClRoZXJlIHdhcyBzb21lIG92ZXJsYXAgYmV0d2VlbiBjb3Vyc2VzL3NjaG9vbCB5ZWFycywgc28gd2UgY2FuJ3Qgc2ltcGx5IGFkZCB0aGVzZSBudW1iZXJzLgoKQnkgY291cnNlOgpgYGB7cn0KY291bnRzWywgLih1bmlxdWVfdXNlcnMgPSBsZW5ndGgodW5pcXVlKHVzZXIpKSksIGJ5ID0gLihjb3Vyc2UpXQpgYGAKCgpCeSBzY2hvb2wgeWVhciBpbiB0aGUgRnJlbmNoIGFuZCBFbmdsaXNoIGNvdXJzZXM6CmBgYHtyfQpjb3VudHNbY291cnNlICVpbiUgYygiRnJlbmNoIiwgIkVuZ2xpc2giKSwgLih1bmlxdWVfdXNlcnMgPSBsZW5ndGgodW5pcXVlKHVzZXIpKSksIGJ5ID0gLihzY2hvb2xfeWVhcildCmBgYAoKQW5kIHRvdGFsIG51bWJlciBvZiB1bmlxdWUgdXNlcnMgaW4gdGhlIEZyZW5jaCBhbmQgRW5nbGlzaCBzYW1wbGUgYWNyb3NzIGJvdGggeWVhcnM6CmBgYHtyfQpjb3VudHNbY291cnNlICVpbiUgYygiRnJlbmNoIiwgIkVuZ2xpc2giKSwgLih1bmlxdWVfdXNlcnMgPSBsZW5ndGgodW5pcXVlKHVzZXIpKSldCmBgYAoKCiMjIFRvdGFsIG51bWJlciBvZiB0cmlhbHMgYnkgY291cnNlCmBgYHtyfQpjb3VudHNbLCAuKHRvdGFsX3RyaWFscyA9IHN1bSh0cmlhbHMpKSwgYnkgPSAuKGNvdXJzZSwgc2Nob29sX3llYXIpXQpgYGAKCiMjIE51bWJlciBvZiB0cmlhbHMgYnkgdXNlcgoKYGBge3J9CmNvdW50c1ssIHRyaWFsc191c2VyIDo9IHN1bSh0cmlhbHMpLCBieSA9IC4oY291cnNlLCB1c2VyKV0KZ2dwbG90KGNvdW50cywgYWVzKHggPSB0cmlhbHNfdXNlcikpICsKICBmYWNldF93cmFwKH5jb3Vyc2UsIG5jb2wgPSAxLCBzY2FsZXMgPSAiZnJlZV95IikgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMTAwKSArCiAgbGFicyh4ID0gIk51bWJlciBvZiB0cmlhbHMgYnkgdXNlciIsCiAgICAgICB5ID0gTlVMTCkgKwogIHRoZW1lX3BhcGVyCgpgYGAKCiMjIE51bWJlciBvZiB1bmlxdWUgZGF5cyBieSB1c2VyCmBgYHtyfQpnZ3Bsb3QoY291bnRzWywgLihOID0gbGVuZ3RoKHVuaXF1ZShkb3lfcG9zaXgpKSksIGJ5ID0gYygidXNlciIsICJjb3Vyc2UiKV0sIGFlcyh4ID0gTikpICsKICBmYWNldF9ncmlkKGNvdXJzZSB+IC4sIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKSArCiAgbGFicyh4ID0gIkFhbnRhbCBkYWdlbiBtZXQgbGVlcmFjdGl2aXRlaXQiLAogICAgICAgeSA9ICJBYW50YWwgbGVlcmxpbmdlbiIpICsKICB0aGVtZV9wYXBlcgpgYGAKCgojIyBUb3RhbCBudW1iZXIgb2YgdHJpYWxzIGJ5IGRheQoKSW50ZXJwb2xhdGUgbWlzc2luZyBkYXlzOgpgYGB7cn0KZG95X3Bvc2l4IDwtIHNlcS5QT1NJWHQoZnJvbSA9IGNvdW50c1ssbWluKGRveV9wb3NpeCldLCB0byA9IGNvdW50c1ssbWF4KGRveV9wb3NpeCldLCBieSA9ICJEU1RkYXkiKQpjb3Vyc2UgPC0gY291bnRzWyx1bmlxdWUoY291cnNlKV0KZGF0ZXMgPC0gQ0ooZG95X3Bvc2l4LCBjb3Vyc2UpCmNvdW50cyA8LSBtZXJnZShjb3VudHMsIGRhdGVzLCBieSA9IGMoImRveV9wb3NpeCIsICJjb3Vyc2UiKSwgYWxsID0gVFJVRSkKYGBgCgpDb3VudCB0cmlhbHMgYnkgZGF5OgpgYGB7cn0KY291bnRzWywgdHJpYWxzX3RvdGFsIDo9IHN1bSh0cmlhbHMsIG5hLnJtID0gVFJVRSksIGJ5ID0gLihjb3Vyc2UsIGRveV9wb3NpeCldCmBgYAoKYGBge3J9CmNvdW50c19ieV9kYXkgPC0gY291bnRzWywgLih0cmlhbHNfdG90YWwgPSBzdW0odHJpYWxzLCBuYS5ybSA9IFRSVUUpKSwgYnkgPSAuKGNvdXJzZSwgZG95X3Bvc2l4KV0KYGBgCgoKYGBge3J9CmdncGxvdChjb3VudHNfYnlfZGF5W2NvdXJzZSAlaW4lIGMoIkVuZ2xpc2giLCAiRnJlbmNoIiksXSwKICAgICAgIGFlcyh4ID0gZG95X3Bvc2l4LCB5ID0gdHJpYWxzX3RvdGFsLCBjb2xvdXIgPSBjb3Vyc2UpKSArCiAgZ2VvbV9saW5lKCkgKwogIHNjYWxlX3hfZGF0ZXRpbWUoZGF0ZV9icmVha3MgPSAiMyBtb250aHMiLCBkYXRlX2xhYmVscyA9ICIlZSAlYiAlWSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gbnVtYmVyX2Zvcm1hdCkgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIk51bWJlciBvZiB0cmlhbHMgcGVyIGRheSIsCiAgICAgICBjb2xvdXIgPSAiQ291cnNlIikgKwogIHRoZW1lX3BhcGVyCmBgYAoKCiMjIFRvdGFsIG51bWJlciBvZiB0cmlhbHMgYnkgd2VlawoKVXNlIGN1dC5EYXRlKCkgdG8gYmluIGRhdGVzIGJ5IHdlZWsuIEVhY2ggZGF5IGlzIGFzc2lnbmVkIHRoZSBkYXRlIG9mIHRoZSBtb3N0IHJlY2VudCBNb25kYXkuCmBgYHtyfQpjb3VudHNfYnlfZGF5WywgZG95X3Bvc2l4X3dlZWsgOj0gY3V0LlBPU0lYdChkb3lfcG9zaXgsICJ3ZWVrIildCmNvdW50c19ieV9kYXlbLCB0cmlhbHNfdG90YWxfd2VlayA6PSBzdW0odHJpYWxzX3RvdGFsLCBuYS5ybSA9IFRSVUUpLCBieSA9IC4oY291cnNlLCBkb3lfcG9zaXhfd2VlayldCmBgYAoKCmBgYHtyfQpnZ3Bsb3QoY291bnRzX2J5X2RheVtjb3Vyc2UgJWluJSBjKCJFbmdsaXNoIiwgIkZyZW5jaCIpLF0sCiAgICAgICAgICAgIGFlcyh4ID0gZG95X3Bvc2l4LCB5ID0gdHJpYWxzX3RvdGFsX3dlZWssIGNvbG91ciA9IGNvdXJzZSkpICsKICBnZW9tX2xpbmUoKSArCiAgc2NhbGVfeF9kYXRldGltZShkYXRlX2JyZWFrcyA9ICIzIG1vbnRocyIsIGRhdGVfbGFiZWxzID0gIiVlICViICVZIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBudW1iZXJfZm9ybWF0KSArCiAgbGFicyh4ID0gTlVMTCwKICAgICAgIHkgPSAiTnVtYmVyIG9mIHRyaWFscyBwZXIgd2VlayIsCiAgICAgICBjb2xvdXIgPSAiQ291cnNlIikgKwogIHRoZW1lX3BhcGVyCmBgYAoKCk92ZXJsYXAgdGhlIHR3byBzY2hvb2wgeWVhcnM6CmBgYHtyfQpjb3VudHNfYnlfZGF5Wywgc2Nob29sX3llYXIgOj0gaWZlbHNlKGRveV9wb3NpeCA8ICIyMDE5LTA4LTAxIiwgIjE4LzE5IiwgIjE5LzIwIildCmNvdW50c19ieV9kYXlbc2Nob29sX3llYXIgPT0gIjE4LzE5IiwgZG95X3Bvc2l4X2FsaWduZWQgOj0gYXMuUE9TSVhjdChkb3lfcG9zaXggKyAzNjUqMjQqNjAqNjAsIG9yaWdpbiA9ICIxOTcwLTAxLTAxIildCmNvdW50c19ieV9kYXlbc2Nob29sX3llYXIgPT0gIjE5LzIwIiwgZG95X3Bvc2l4X2FsaWduZWQgOj0gZG95X3Bvc2l4XQpgYGAKCmBgYHtyfQpwX3RyaWFsX2hpc3QgPC0gZ2dwbG90KGNvdW50c19ieV9kYXlbY291cnNlICVpbiUgYygiRW5nbGlzaCIsICJGcmVuY2giKSxdLAogICAgICAgICAgICBhZXMoeCA9IGRveV9wb3NpeF9hbGlnbmVkLCB5bWluID0gMCwgeW1heCA9IHRyaWFsc190b3RhbF93ZWVrLCBncm91cCA9IHNjaG9vbF95ZWFyLCBjb2xvdXIgPSBzY2hvb2xfeWVhciwgZmlsbCA9IHNjaG9vbF95ZWFyKSkgKwogIGZhY2V0X3dyYXAofiBjb3Vyc2UsIG5jb2wgPSAxKSArCiAgZ2VvbV9yZWN0KHhtaW4gPSBkYXRlX3NjaG9vbHNfY2xvc2VkLCB4bWF4ID0gZGF0ZV9zY2hvb2xzX29wZW5lZCwgeW1pbiA9IC0yZTUsIHltYXggPSAyLjJlNiwgZmlsbCA9ICJncmV5OTIiLCBjb2xvdXIgPSAiZ3JleTUwIiwgbHR5ID0gMikgKwogIGdlb21fcmliYm9uKGFscGhhID0gLjIpICsKICBzY2FsZV94X2RhdGV0aW1lKGV4cGFuZCA9IGMoMCwgMCksIAogICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYXMuUE9TSVhjdChjKAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0wOS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0xMS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wMS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wMy0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNy0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBhcy5QT1NJWGN0KGMoIjIwMTktMDktMDEgMDI6MDA6MDAgQ0VUIiwgIjIwMjAtMDctMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoMCwgMmU2KSwgbGFiZWxzID0gbnVtYmVyX2Zvcm1hdCkgKwogIHNjYWxlX2NvbG91cl92aXJpZGlzX2QoZW5kID0gLjUsIGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKGVuZCA9IC41LCBkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJUcmlhbHMgcGVyIHdlZWsiLAogICAgICAgY29sb3VyID0gIlNjaG9vbCB5ZWFyIiwKICAgICAgIGZpbGwgPSAiU2Nob29sIHllYXIiKSArCiAgdGhlbWVfcGFwZXIKCnBfdHJpYWxfaGlzdAoKZ2dzYXZlKCIuLi9vdXRwdXQvdHJpYWxfaGlzdC5wZGYiLCB3aWR0aCA9IDUsIGhlaWdodCA9IDMpCmdnc2F2ZSgiLi4vb3V0cHV0L3RyaWFsX2hpc3QuZXBzIiwgd2lkdGggPSA1LCBoZWlnaHQgPSAzKQpnZ3NhdmUoIi4uL291dHB1dC90cmlhbF9oaXN0LnBuZyIsIHdpZHRoID0gNSwgaGVpZ2h0ID0gMykKYGBgCgpNYWtlIGEgbGluZS1wbG90IHZlcnNpb24gb2YgdGhlIGhpc3RvZ3JhbS4KYGBge3J9CnBfdHJpYWxfaGlzdF9saW5lIDwtIGdncGxvdChjb3VudHNfYnlfZGF5W2NvdXJzZSAlaW4lIGMoIkVuZ2xpc2giLCAiRnJlbmNoIildLAogICAgICAgICAgICBhZXMoeCA9IGRveV9wb3NpeF9hbGlnbmVkLCB5ID0gdHJpYWxzX3RvdGFsX3dlZWssIGdyb3VwID0gc2Nob29sX3llYXIsIGNvbG91ciA9IHNjaG9vbF95ZWFyLCBmaWxsID0gc2Nob29sX3llYXIpKSArCiAgZmFjZXRfd3JhcCh+IGNvdXJzZSwgbmNvbCA9IDEpICsKICBnZW9tX3JlY3QoeG1pbiA9IGRhdGVfc2Nob29sc19jbG9zZWQsIHhtYXggPSBkYXRlX3NjaG9vbHNfb3BlbmVkLCB5bWluID0gLTJlNSwgeW1heCA9IDIuMmU2LCBmaWxsID0gImdyZXk5MiIsIGNvbG91ciA9ICJncmV5NTAiLCBsdHkgPSAyKSArCiAgZ2VvbV9saW5lKCkgKwogIHNjYWxlX3hfZGF0ZXRpbWUoZXhwYW5kID0gYygwLCAwKSwgCiAgICAgICAgICAgICAgICAgICBicmVha3MgPSBhcy5QT1NJWGN0KGMoCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTA5LTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTExLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTAxLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTAzLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA1LTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA3LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGFzLlBPU0lYY3QoYygiMjAxOS0wOS0wMSAwMjowMDowMCBDRVQiLCAiMjAyMC0wNy0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwgbGltaXRzID0gYygwLCAyZTYpLCBsYWJlbHMgPSBudW1iZXJfZm9ybWF0KSArCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfZChlbmQgPSAuNSwgZGlyZWN0aW9uID0gLTEsIG5hLnRyYW5zbGF0ZSA9IEZBTFNFKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoZW5kID0gLjUsIGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIlRyaWFscyBwZXIgd2VlayIsCiAgICAgICBjb2xvdXIgPSAiU2Nob29sIHllYXIiLAogICAgICAgZmlsbCA9ICJTY2hvb2wgeWVhciIpICsKICB0aGVtZV9wYXBlcgoKcF90cmlhbF9oaXN0X2xpbmUKCmdnc2F2ZSgiLi4vb3V0cHV0L3RyaWFsX2hpc3RfbGluZS5wZGYiLCB3aWR0aCA9IDUsIGhlaWdodCA9IDMpCmdnc2F2ZSgiLi4vb3V0cHV0L3RyaWFsX2hpc3RfbGluZS5lcHMiLCB3aWR0aCA9IDUsIGhlaWdodCA9IDMpCmdnc2F2ZSgiLi4vb3V0cHV0L3RyaWFsX2hpc3RfbGluZS5wbmciLCB3aWR0aCA9IDUsIGhlaWdodCA9IDMpCmBgYAoKQWxzbyBtYWtlIGEgZGlmZmVyZW5jZSBwbG90LgpgYGB7cn0KIyBJbiBvcmRlciBmb3IgdGhlIE1vbmRheXMgdG8gYWxpZ24sIG1vdmUgdGhlIDE4LzE5IGRhdGEgZm9yd2FyZCBieSAxIHllYXIgLSAxIGRheS4KY291bnRzX2J5X2RheVtzY2hvb2xfeWVhciA9PSAiMTgvMTkiLCBkb3lfcG9zaXhfYWxpZ25lZCA6PSBhcy5QT1NJWGN0KGRveV9wb3NpeCArIDM2NCoyNCo2MCo2MCwgb3JpZ2luID0gIjE5NzAtMDEtMDEiKV0KY291bnRzX2J5X2RheVtzY2hvb2xfeWVhciA9PSAiMTkvMjAiLCBkb3lfcG9zaXhfYWxpZ25lZCA6PSBkb3lfcG9zaXhdCmBgYAoKYGBge3J9CmNvdW50c19ieV9kYXlbLCB5ZWFyX2RpZmYgOj0gdHJpYWxzX3RvdGFsX3dlZWtbMl0gLSB0cmlhbHNfdG90YWxfd2Vla1sxXSwgYnkgPSAuKGNvdXJzZSwgZG95X3Bvc2l4X2FsaWduZWQpXQoKZ2dwbG90KGNvdW50c19ieV9kYXlbY291cnNlICVpbiUgYygiRW5nbGlzaCIsICJGcmVuY2giKV0sCiAgICAgICAgICAgIGFlcyh4ID0gZG95X3Bvc2l4X2FsaWduZWQsIHkgPSB5ZWFyX2RpZmYpKSArCiAgZmFjZXRfd3JhcCh+IGNvdXJzZSwgbmNvbCA9IDEpICsKICBnZW9tX3JlY3QoeG1pbiA9IGRhdGVfc2Nob29sc19jbG9zZWQsIHhtYXggPSBkYXRlX3NjaG9vbHNfb3BlbmVkLCB5bWluID0gLTFlNiwgeW1heCA9IDEuMWU2LCBmaWxsID0gImdyZXk5MiIsIGNvbG91ciA9ICJncmV5NTAiLCBsdHkgPSAyKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbHR5ID0gMykgKwogIGdlb21fbGluZSgpICsKICBzY2FsZV94X2RhdGV0aW1lKGV4cGFuZCA9IGMoMCwgMCksIAogICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYXMuUE9TSVhjdChjKAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0wOS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0xMS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wMS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wMy0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNy0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBhcy5QT1NJWGN0KGMoIjIwMTktMDktMDEgMDI6MDA6MDAgQ0VUIiwgIjIwMjAtMDctMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoLTNlNSwgMWU2KSwgbGFiZWxzID0gbnVtYmVyX2Zvcm1hdCkgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIlRyaWFscyBwZXIgd2VlayIsCiAgICAgICBjb2xvdXIgPSAiU2Nob29sIHllYXIiLAogICAgICAgZmlsbCA9ICJTY2hvb2wgeWVhciIpICsKICB0aGVtZV9wYXBlcgpgYGAKCgojIyBOdW1iZXIgb2YgdHJpYWxzIGJ5IHVzZXIgYW5kIHdlZWsKYGBge3J9CmNvdW50c1ssIGRveV9wb3NpeF93ZWVrIDo9IGN1dC5QT1NJWHQoZG95X3Bvc2l4LCAid2VlayIpXQpjb3VudHNfYnlfdXNlcl9hbmRfd2VlayA8LSBjb3VudHNbLCAuKHRyaWFsc191c2VyID0gc3VtKHRyaWFscywgbmEucm0gPSBUUlVFKSksIGJ5ID0gLihjb3Vyc2UsIHNjaG9vbF95ZWFyLCB1c2VyLCBkb3lfcG9zaXhfd2VlayldCmBgYAoKU2F2ZSBmb3IgY2x1c3RlcmluZyBhbmFseXNpcwpgYGB7cn0Kc2F2ZVJEUyhuYS5vbWl0KGNvdW50c19ieV91c2VyX2FuZF93ZWVrW2NvdXJzZSAlaW4lIGMoIkVuZ2xpc2giLCAiRnJlbmNoIildKSwgIi4uL2RhdGEvdHJpYWxzX2J5X3VzZXJfYW5kX3dlZWsucmRzIikKYGBgCgoKIyMgVW5pcXVlIHVzZXJzIGJ5IGRheQoKYGBge3J9CnVzZXJzX2J5X2RheSA8LSBjb3VudHNbLCAuKHVuaXF1ZV91c2VycyA9IGxlbmd0aCh1bmlxdWUodXNlcikpKSwgYnkgPSAuKGNvdXJzZSwgZG95X3Bvc2l4KV0KYGBgCgpgYGB7cn0KcCA8LSBnZ3Bsb3QodXNlcnNfYnlfZGF5LCBhZXMoeCA9IGRveV9wb3NpeCwgeSA9IHVuaXF1ZV91c2VycywgY29sb3VyID0gY291cnNlKSkgKwogIGdlb21fbGluZSgpICsKICBzY2FsZV94X2RhdGV0aW1lKGRhdGVfYnJlYWtzID0gIjMgbW9udGhzIiwgZGF0ZV9sYWJlbHMgPSAiJWUgJWIgJVkiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IG51bWJlcl9mb3JtYXQpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJOdW1iZXIgb2YgdXNlcnMgcGVyIGRheSIsCiAgICAgICBjb2xvdXIgPSAiQ291cnNlIikgKwogIHRoZW1lX3BhcGVyCgpwCmBgYAoKCiMjIFVuaXF1ZSB1c2VycyBieSB3ZWVrCgpVc2UgY3V0LkRhdGUoKSB0byBiaW4gZGF0ZXMgYnkgd2Vlay4gRWFjaCBkYXkgaXMgYXNzaWduZWQgdGhlIGRhdGUgb2YgdGhlIG1vc3QgcmVjZW50IE1vbmRheS4KYGBge3J9CnVzZXJzX2J5X2RheVssIGRveV9wb3NpeF93ZWVrIDo9IGN1dC5QT1NJWHQoZG95X3Bvc2l4LCAid2VlayIpXQpgYGAKCmBgYHtyfQp1c2Vyc19ieV93ZWVrIDwtIGNvdW50c1ssIC4odW5pcXVlX3VzZXJzX3dlZWsgPSBsZW5ndGgodW5pcXVlKHVzZXIpKSksIGJ5ID0gLihjb3Vyc2UsIGRveV9wb3NpeF93ZWVrKV0KdXNlcnNfYnlfd2VlayA8LSB1c2Vyc19ieV9kYXlbdXNlcnNfYnlfd2Vlaywgb24gPSAuKGNvdXJzZSwgZG95X3Bvc2l4X3dlZWspXQpgYGAKCmBgYHtyfQpwIDwtIGdncGxvdCh1c2Vyc19ieV93ZWVrLCBhZXMoeCA9IGRveV9wb3NpeCwgeW1pbiA9IDAsIHltYXggPSB1bmlxdWVfdXNlcnNfd2VlaywgZ3JvdXAgPSBjb3Vyc2UsIGNvbG91ciA9IGNvdXJzZSwgZmlsbCA9IGNvdXJzZSkpICsKICBmYWNldF93cmFwKH4gY291cnNlLCBuY29sID0gMSwgc2NhbGVzID0gImZyZWVfeSIpICsKICBnZW9tX3JpYmJvbihhbHBoYSA9IC4yKSArCiAgc2NhbGVfeF9kYXRldGltZShkYXRlX2JyZWFrcyA9ICIzIG1vbnRocyIsIGRhdGVfbGFiZWxzID0gIiVlICViICVZIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6Om51bWJlcl9mb3JtYXQoYmlnLm1hcmsgPSAiLiIsIGRlY2ltYWwubWFyayA9ICIsIikpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJBYW50YWwgZ2VicnVpa2VycyIsCiAgICAgICB0aXRsZSA9ICJBYW50YWwgdmVyc2NoaWxsZW5kZSBnZWJydWlrZXJzIHBlciB3ZWVrIiwKICAgICAgIGNhcHRpb24gPSAiTGV0IG9wOiBzY2hhYWwgdmVyc2NoaWx0IHR1c3NlbiBkZSBncmFmaWVrZW4iLAogICAgICAgY29sb3VyID0gIkxlc21ldGhvZGUiLAogICAgICAgZmlsbCA9ICJMZXNtZXRob2RlIikgKwogIGd1aWRlcyhjb2xvdXIgPSBGQUxTRSwgZmlsbCA9IEZBTFNFKSArCiAgdGhlbWVfcGFwZXIKCnAKYGBgCgpPdmVybGFwIHRoZSB0d28gc2Nob29sIHllYXJzOgpgYGB7cn0KdXNlcnNfYnlfd2Vla1ssIHNjaG9vbF95ZWFyIDo9IGlmZWxzZShkb3lfcG9zaXggPCAiMjAxOS0wOC0wMSIsICIxOC8xOSIsICIxOS8yMCIpXQp1c2Vyc19ieV93ZWVrW3NjaG9vbF95ZWFyID09ICIxOC8xOSIsIGRveV9wb3NpeF9hbGlnbmVkIDo9IGFzLlBPU0lYY3QoZG95X3Bvc2l4ICsgMzY1KjI0KjYwKjYwLCBvcmlnaW4gPSAiMTk3MC0wMS0wMSIpXQp1c2Vyc19ieV93ZWVrW3NjaG9vbF95ZWFyID09ICIxOS8yMCIsIGRveV9wb3NpeF9hbGlnbmVkIDo9IGRveV9wb3NpeF0KYGBgCgoKYGBge3J9CnBfdXNlcl9oaXN0IDwtIGdncGxvdCh1c2Vyc19ieV93ZWVrW2NvdXJzZSAlaW4lIGMoIkVuZ2xpc2giLCAiRnJlbmNoIiksXSwKICAgICAgICAgICAgYWVzKHggPSBkb3lfcG9zaXhfYWxpZ25lZCwgeW1pbiA9IDAsIHltYXggPSB1bmlxdWVfdXNlcnNfd2VlaywgZ3JvdXAgPSBzY2hvb2xfeWVhciwgY29sb3VyID0gc2Nob29sX3llYXIsIGZpbGwgPSBzY2hvb2xfeWVhcikpICsKICBmYWNldF93cmFwKH4gY291cnNlLCBuY29sID0gMSkgKwogIGdlb21fcmVjdCh4bWluID0gZGF0ZV9zY2hvb2xzX2Nsb3NlZCwgeG1heCA9IGRhdGVfc2Nob29sc19vcGVuZWQsIHltaW4gPSAtODAwLCB5bWF4ID0gODgwMCwgZmlsbCA9ICJncmV5OTIiLCBjb2xvdXIgPSAiZ3JleTUwIiwgbHR5ID0gMikgKwogIGdlb21fcmliYm9uKGFscGhhID0gLjIpICsKICBzY2FsZV94X2RhdGV0aW1lKGV4cGFuZCA9IGMoMCwgMCksIAogICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYXMuUE9TSVhjdChjKAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0wOS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0xMS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wMS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wMy0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNy0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBhcy5QT1NJWGN0KGMoIjIwMTktMDktMDEgMDI6MDA6MDAgQ0VUIiwgIjIwMjAtMDctMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoMCwgODAwMCksIGxhYmVscyA9IG51bWJlcl9mb3JtYXQpICsKICBzY2FsZV9jb2xvdXJfdmlyaWRpc19kKGVuZCA9IC41LCBkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZChlbmQgPSAuNSwgZGlyZWN0aW9uID0gLTEsIG5hLnRyYW5zbGF0ZSA9IEZBTFNFKSArCiAgbGFicyh4ID0gTlVMTCwKICAgICAgIHkgPSAiVW5pcXVlIHVzZXJzIHBlciB3ZWVrIiwKICAgICAgIGNvbG91ciA9ICJTY2hvb2wgeWVhciIsCiAgICAgICBmaWxsID0gIlNjaG9vbCB5ZWFyIikgKwogIHRoZW1lX3BhcGVyCgpwX3VzZXJfaGlzdAoKZ2dzYXZlKCIuLi9vdXRwdXQvdXNlcl9oaXN0LnBkZiIsIHdpZHRoID0gNSwgaGVpZ2h0ID0gMykKZ2dzYXZlKCIuLi9vdXRwdXQvdXNlcl9oaXN0LmVwcyIsIHdpZHRoID0gNSwgaGVpZ2h0ID0gMykKZ2dzYXZlKCIuLi9vdXRwdXQvdXNlcl9oaXN0LnBuZyIsIHdpZHRoID0gNSwgaGVpZ2h0ID0gMykKYGBgCgoKTWFrZSBhIGNvbWJpbmVkIHBsb3Qgb2YgdHJpYWwgYW5kIHVzZXIgY291bnRzIGZvciBpbiB0aGUgcGFwZXI6CmBgYHtyfQpwX2xlZ2VuZCA8LSBnZXRfbGVnZW5kKHBfdHJpYWxfaGlzdCkKCnBfdHJpYWxfaGlzdCA8LSBwX3RyaWFsX2hpc3QgKwogIGd1aWRlcyhjb2xvdXIgPSBGQUxTRSwgZmlsbCA9IEZBTFNFKQoKcF91c2VyX2hpc3QgPC0gcF91c2VyX2hpc3QgKwogIGd1aWRlcyhjb2xvdXIgPSBGQUxTRSwgZmlsbCA9IEZBTFNFKQpgYGAKCmBgYHtyfQpwbG90X2dyaWQocGxvdF9ncmlkKHBfdHJpYWxfaGlzdCwgcF91c2VyX2hpc3QsCiAgICAgICAgICBsYWJlbHMgPSBjKCJBIiwgIkIiKSwKICAgICAgICAgIGFsaWduID0gInYiLCBheGlzID0gInRibHIiKSwKICAgICAgICAgIHBfbGVnZW5kLAogICAgICAgICAgcmVsX3dpZHRocyA9IGMoMSwgLjIpKQoKZ2dzYXZlKCIuLi9vdXRwdXQvY29tYmlfaGlzdC5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDMpCmdnc2F2ZSgiLi4vb3V0cHV0L2NvbWJpX2hpc3QuZXBzIiwgd2lkdGggPSA5LCBoZWlnaHQgPSAzKQpnZ3NhdmUoIi4uL291dHB1dC9jb21iaV9oaXN0LnBuZyIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gMykKYGBgCgoKIyMgQWN0aXZpdHkgZHVyaW5nIHRoZSB3ZWVrCgpHZXQgbnVtYmVyIG9mIHRyaWFscyBieSBtZXRob2QsIGRheSwgaG91ciwgYW5kIHVzZXI6CmBgYHtyfQpkYiA8LSBkYl9jb25uZWN0KCkKY291bnRzX2J5X2hvdXIgPC0gZGJHZXRRdWVyeShkYiwiU0VMRUNUIHIubWV0aG9kIEFTICdtZXRob2QnLAogICAgICAgICAgICAgICAgICAgICAgICAgIERBVEUoci5kYXRlICsgMzYwMCwgJ3VuaXhlcG9jaCcpIEFTICdkb3knLAogICAgICAgICAgICAgICAgICAgICAgICAgIFNUUkZUSU1FKCclSCcsIHIuZGF0ZSArIDM2MDAsICd1bml4ZXBvY2gnKSBBUyAnaG91cicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgci51c2VyX2lkIEFTICd1c2VyJywKICAgICAgICAgICAgICAgICAgICAgICAgICBDT1VOVCgqKSBBUyAndHJpYWxzJwogICAgICAgICAgICAgICAgICAgICAgICAgIEZST00gJ3Jlc3BvbnNlc19ub2R1cGxpY2F0ZXMnIHIKICAgICAgICAgICAgICAgICAgICAgICAgICBHUk9VUCBCWSByLm1ldGhvZCwKICAgICAgICAgICAgICAgICAgICAgICAgICBEQVRFKHIuZGF0ZSArIDM2MDAsICd1bml4ZXBvY2gnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBTVFJGVElNRSgnJUgnLCByLmRhdGUgKyAzNjAwLCAndW5peGVwb2NoJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgci51c2VyX2lkCiAgICAgICAgICAgICAgICAgICAgICAgICIpCmRiX2Rpc2Nvbm5lY3QoZGIpCgpzZXREVChjb3VudHNfYnlfaG91cikKYGBgCgpJbnRlcnBvbGF0ZSBtaXNzaW5nIGRheXMgYW5kIGhvdXJzOgpgYGB7cn0KY291bnRzX2J5X2hvdXJbLCBkb3lfcG9zaXggOj0gYXMuUE9TSVhjdChkb3kpXQpjb3VudHNfYnlfaG91clssIGhvdXIgOj0gYXMubnVtZXJpYyhob3VyKV0KZG95X3Bvc2l4IDwtIHNlcS5QT1NJWHQoZnJvbSA9IGNvdW50c19ieV9ob3VyWyxtaW4oZG95X3Bvc2l4KV0sIHRvID0gY291bnRzX2J5X2hvdXJbLG1heChkb3lfcG9zaXgpXSwgYnkgPSAiRFNUZGF5IikKbWV0aG9kIDwtIGNvdW50c19ieV9ob3VyWyx1bmlxdWUobWV0aG9kKV0KaG91ciA8LSAwOjIzCmRhdGVzX2FuZF9ob3VycyA8LSBDSihkb3lfcG9zaXgsIGhvdXIsIG1ldGhvZCkKY291bnRzX2J5X2hvdXIgPC0gbWVyZ2UoY291bnRzX2J5X2hvdXIsIGRhdGVzX2FuZF9ob3VycywgYnkgPSBjKCJkb3lfcG9zaXgiLCAiaG91ciIsICJtZXRob2QiKSwgYWxsID0gVFJVRSkKYGBgCgpBZGQgZGF5IG9mIHRoZSB3ZWVrOgpgYGB7cn0KY291bnRzX2J5X2hvdXJbLCB3ZWVrZGF5IDo9IHdlZWtkYXlzKGRveV9wb3NpeCldCmBgYAoKRGlzdGluZ3Vpc2ggYmV0d2VlbiBzY2hvb2wgeWVhcnM6CmBgYHtyfQpjb3VudHNfYnlfaG91clssIHNjaG9vbF95ZWFyIDo9IGlmZWxzZShkb3lfcG9zaXggPCAiMjAxOS0wOC0wMSIsICIxOC8xOSIsICIxOS8yMCIpXQpgYGAKCkFkZCBxdWFydGVyOgpgYGB7cn0KY291bnRzX2J5X2hvdXJbLCBxdWFydGVyIDo9IHBhc3RlMCh5ZWFyKGRveV9wb3NpeCksICJRIiwgcXVhcnRlcihkb3lfcG9zaXgpKV0KYGBgCgoKQWRkIGV4YWN0IHNjaG9vbCBjbG9zdXJlIHBlcmlvZCBpbiBib3RoIHNjaG9vbCB5ZWFyczoKYGBge3J9CmNvdW50c19ieV9ob3VyW3NjaG9vbF95ZWFyID09ICIxOC8xOSIsIGRveV9wb3NpeF9hbGlnbmVkIDo9IGFzLlBPU0lYY3QoZG95X3Bvc2l4ICsgMzY1KjI0KjYwKjYwLCBvcmlnaW4gPSAiMTk3MC0wMS0wMSIpXQpjb3VudHNfYnlfaG91cltzY2hvb2xfeWVhciA9PSAiMTkvMjAiLCBkb3lfcG9zaXhfYWxpZ25lZCA6PSBkb3lfcG9zaXhdCgpjb3VudHNfYnlfaG91clssIHNjaG9vbHNfY2xvc2VkIDo9IGRveV9wb3NpeF9hbGlnbmVkID49IGRhdGVfc2Nob29sc19jbG9zZWQgJiBkb3lfcG9zaXhfYWxpZ25lZCA8IGRhdGVfc2Nob29sc19vcGVuZWRdCmBgYAoKCkFkZCBtb3JlIHNlbnNpYmxlIGNvdXJzZSBuYW1lczoKYGBge3J9CmNvdW50c19ieV9ob3VyWywgY291cnNlIDo9IGlmZWxzZShtZXRob2QgPT0gIkdyYW5kZXMgTGlnbmVzIiwgIkZyZW5jaCIsIGlmZWxzZShtZXRob2QgPT0gIlN0ZXBwaW5nIFN0b25lcyIsICJFbmdsaXNoIiwgIkdlcm1hbiIpKV0KYGBgCgoKU3VtIHRyaWFscyBieSBzY2hvb2wgeWVhciwgd2Vla2RheSBhbmQgaG91cjoKYGBge3J9CmNvdW50c19ieV9ob3VyWywgdHJpYWxzX3NjaG9vbHllYXIgOj0gc3VtKHRyaWFscywgbmEucm0gPSBUUlVFKSwgYnkgPSAuKGNvdXJzZSwgc2Nob29sX3llYXIsIHdlZWtkYXksIGhvdXIpXQpgYGAKCkFsc28gc3VtIHRyaWFscyBieSBxdWFydGVyLCB3ZWVrZGF5IGFuZCBob3VyOgpgYGB7cn0KY291bnRzX2J5X2hvdXJbLCB0cmlhbHNfcXVhcnRlciA6PSBzdW0odHJpYWxzLCBuYS5ybSA9IFRSVUUpLCBieSA9IC4oY291cnNlLCBxdWFydGVyLCB3ZWVrZGF5LCBob3VyKV0KYGBgCgpBbmQgc3VtIHRyaWFscyB3aXRoaW4gdGhlIGNsb3N1cmUgcGVyaW9kIGJ5IHdlZWtkYXkgYW5kIGhvdXI6CmBgYHtyfQpjb3VudHNfYnlfaG91cltzY2hvb2xzX2Nsb3NlZCA9PSBUUlVFLCB0cmlhbHNfY2xvc2VkIDo9IHN1bSh0cmlhbHMsIG5hLnJtID0gVFJVRSksIGJ5ID0gLihjb3Vyc2UsIHNjaG9vbF95ZWFyLCB3ZWVrZGF5LCBob3VyKV0KYGBgCgoKYGBge3J9CnRyaWFsc19ieV93ZGF5X2hvdXIgPC0gdW5pcXVlKGNvdW50c19ieV9ob3VyLCBieSA9IGMoImNvdXJzZSIsICJzY2hvb2xfeWVhciIsICJxdWFydGVyIiwgInNjaG9vbHNfY2xvc2VkIiwgIndlZWtkYXkiLCAiaG91ciIpKQoKdHJpYWxzX2J5X3dkYXlfaG91clssIHRyaWFsc19ub3JtYWxpc2VkX3NjaG9vbHllYXIgOj0gdHJpYWxzX3NjaG9vbHllYXIgLyBzdW0odHJpYWxzX3NjaG9vbHllYXIpLCBieSA9IC4oY291cnNlKV0KdHJpYWxzX2J5X3dkYXlfaG91clssIHRyaWFsc19ub3JtYWxpc2VkX3F1YXJ0ZXIgOj0gdHJpYWxzX3F1YXJ0ZXIgLyBzdW0odHJpYWxzX3F1YXJ0ZXIpLCBieSA9IC4oY291cnNlKV0KCnRyaWFsc19ieV93ZGF5X2hvdXJbLCB3ZWVrZGF5IDo9IG9yZGVyZWQod2Vla2RheSwgbGV2ZWxzID0gYygiTW9uZGF5IiwgIlR1ZXNkYXkiLCAiV2VkbmVzZGF5IiwgIlRodXJzZGF5IiwgIkZyaWRheSIsICJTYXR1cmRheSIsICJTdW5kYXkiKSldCiMgdHJpYWxzX2J5X3dkYXlfaG91clssIHdlZWtkYXkgOj0gb3JkZXJlZCh3ZWVrZGF5LCBsZXZlbHMgPSBjKCJtYWFuZGFnIiwgImRpbnNkYWciLCAid29lbnNkYWciLCAiZG9uZGVyZGFnIiwgInZyaWpkYWciLCAiemF0ZXJkYWciLCAiem9uZGFnIikpXQpgYGAKCgpQbG90IGhlYXRtYXAgZm9yIHRoZSB3aG9sZSBzY2hvb2wgeWVhcjoKYGBge3J9CmdncGxvdCh0cmlhbHNfYnlfd2RheV9ob3VyW2NvdXJzZSAlaW4lIGMoIkVuZ2xpc2giLCAiRnJlbmNoIildLAogICAgICAgYWVzKHggPSBob3VyLCB5ID0gcmVvcmRlcih3ZWVrZGF5LCBkcGx5cjo6ZGVzYyh3ZWVrZGF5KSksIGZpbGwgPSB0cmlhbHNfbm9ybWFsaXNlZF9zY2hvb2x5ZWFyKSkgKyAKICBmYWNldF9ncmlkKHNjaG9vbF95ZWFyIH4gY291cnNlKSArCiAgZ2VvbV90aWxlKGNvbG91ciA9ICJ3aGl0ZSIsIHNpemUgPSAwLjI1KSArCiAgbGFicyh4ID0gIlRpbWUgb2YgZGF5IChob3VyKSIsCiAgICAgICB5ID0gTlVMTCkgKwogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsMCksIGJyZWFrcyA9IHNlcSgwLCAyNCwgMykpICsKICBzY2FsZV95X2Rpc2NyZXRlKGV4cGFuZCA9IGMoMCwwKSkgKyAKICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhvcHRpb24gPSAiQSIsIGRpcmVjdGlvbiA9IC0xKSArCiAgY29vcmRfZml4ZWQoKSArCiAgZ3VpZGVzKGZpbGwgPSBGQUxTRSkgKwogIHRoZW1lX3BhcGVyCmBgYAoKUGxvdCBoZWF0bWFwIHBlciBxdWFydGVyOgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxNn0KZ2dwbG90KHRyaWFsc19ieV93ZGF5X2hvdXIsIGFlcyh4ID0gaG91ciwgeSA9IHJlb3JkZXIod2Vla2RheSwgZHBseXI6OmRlc2Mod2Vla2RheSkpLCBmaWxsID0gdHJpYWxzX25vcm1hbGlzZWRfcXVhcnRlcikpICsgCiAgZmFjZXRfZ3JpZChxdWFydGVyIH4gbWV0aG9kKSArCiAgZ2VvbV90aWxlKGNvbG91ciA9ICJ3aGl0ZSIsIHNpemUgPSAwLjI1KSArCiAgbGFicyh4ID0gTlVMTCwKICAgICAgIHkgPSBOVUxMLAogICAgICAgdGl0bGUgPSAiQWN0aXZpdGVpdCBwZXIgdXVyIGdlZHVyZW5kZSBkZSB3ZWVrIiwKICAgICAgIGNhcHRpb24gPSAiQWFudGFsIHRyaWFscyBwZXIgd2Vla2RhZyBlbiB1dXIgaW4gZWxrIGt3YXJ0YWFsLCBnZW5vcm1hbGlzZWVyZCBwZXIgbWV0aG9kZS4iKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwwKSwgYnJlYWtzID0gc2VxKDAsIDI0LCAzKSkgKwogIHNjYWxlX3lfZGlzY3JldGUoZXhwYW5kID0gYygwLDApKSArIAogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbiA9ICJBIiwgZGlyZWN0aW9uID0gLTEpICsKICBjb29yZF9maXhlZCgpICsKICBndWlkZXMoZmlsbCA9IEZBTFNFKSArCiAgdGhlbWVfYncoYmFzZV9zaXplID0gMTYpCmBgYAoKUGxvdCBoZWF0bWFwIGZvciB0aGUgcGVyaW9kIGluIHdoaWNoIHNjaG9vbHMgd2VyZSBjbG9zZWQ6CmBgYHtyfQp0cmlhbHNfY2xvc2VkIDwtIHVuaXF1ZSh0cmlhbHNfYnlfd2RheV9ob3VyW3NjaG9vbHNfY2xvc2VkID09IFRSVUUsIC4oY291cnNlLCBzY2hvb2xfeWVhciwgd2Vla2RheSwgaG91ciwgdHJpYWxzX2Nsb3NlZCldKQoKdHJpYWxzX2Nsb3NlZFssIHRyaWFsc19ub3JtYWxpc2VkX2Nsb3NlZCA6PSB0cmlhbHNfY2xvc2VkIC8gc3VtKHRyaWFsc19jbG9zZWQpLCBieSA9IC4oY291cnNlLCBzY2hvb2xfeWVhcildCmBgYAoKYGBge3J9CnRyaWFsc19jbG9zZWRfZGlmZiA8LSB0cmlhbHNfY2xvc2VkWywgLihzY2hvb2xfeWVhciA9ICJDaGFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJpYWxzX2Nsb3NlZCA9IHRyaWFsc19jbG9zZWRbc2Nob29sX3llYXIgPT0gIjE5LzIwIl0gLSB0cmlhbHNfY2xvc2VkW3NjaG9vbF95ZWFyID09ICIxOC8xOSJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJpYWxzX25vcm1hbGlzZWRfY2xvc2VkID0gdHJpYWxzX25vcm1hbGlzZWRfY2xvc2VkW3NjaG9vbF95ZWFyID09ICIxOS8yMCJdIC0gdHJpYWxzX25vcm1hbGlzZWRfY2xvc2VkW3NjaG9vbF95ZWFyID09ICIxOC8xOSJdKSwgYnkgPSAuKGNvdXJzZSwgd2Vla2RheSwgaG91cildCmBgYAoKCmBgYHtyfQpwX2hlYXRtYXAgPC0gZ2dwbG90KHRyaWFsc19jbG9zZWRbY291cnNlICVpbiUgYygiRW5nbGlzaCIsICJGcmVuY2giKSxdLAogICAgICAgYWVzKHggPSBob3VyLCB5ID0gcmVvcmRlcih3ZWVrZGF5LCBkcGx5cjo6ZGVzYyh3ZWVrZGF5KSksIGZpbGwgPSB0cmlhbHNfbm9ybWFsaXNlZF9jbG9zZWQpKSArIAogIGZhY2V0X2dyaWQoc2Nob29sX3llYXIgfiBjb3Vyc2UpICsKICBnZW9tX3RpbGUoY29sb3VyID0gIndoaXRlIiwgc2l6ZSA9IDAuMjUpICsKICBsYWJzKHggPSAiVGltZSBvZiBkYXkgKGhvdXIpIiwKICAgICAgIHkgPSBOVUxMLAogICAgICAgZmlsbCA9IE5VTEwpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLDApLCBicmVha3MgPSBzZXEoMCwgMjQsIDMpKSArCiAgc2NhbGVfeV9kaXNjcmV0ZShleHBhbmQgPSBjKDAsMCkpICsgCiAgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0gIkEiLCBkaXJlY3Rpb24gPSAtMSkgKwogIGNvb3JkX2ZpeGVkKCkgKwogIHRoZW1lX3BhcGVyCgoKcF9oZWF0bWFwCmBgYAoKCk1ha2UgYSBwbG90IG9mIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHR3byBzY2hvb2wgeWVhcnMgZHVyaW5nIHRoZSBzY2hvb2wgY2xvc3VyZSBwZXJpb2Q6CmBgYHtyfQpwX2hlYXRtYXBfZGlmZiA8LSBnZ3Bsb3QodHJpYWxzX2Nsb3NlZF9kaWZmW2NvdXJzZSAlaW4lIGMoIkVuZ2xpc2giLCAiRnJlbmNoIiksXSwKICAgICAgIGFlcyh4ID0gaG91ciwgeSA9IHJlb3JkZXIod2Vla2RheSwgZHBseXI6OmRlc2Mod2Vla2RheSkpLCBmaWxsID0gdHJpYWxzX25vcm1hbGlzZWRfY2xvc2VkKSkgKyAKICBmYWNldF9ncmlkKHNjaG9vbF95ZWFyIH4gY291cnNlKSArCiAgZ2VvbV90aWxlKGNvbG91ciA9ICJ3aGl0ZSIsIHNpemUgPSAwLjI1KSArCiAgbGFicyh4ID0gIlRpbWUgb2YgZGF5IChob3VyKSIsCiAgICAgICB5ID0gTlVMTCwKICAgICAgIGZpbGwgPSBOVUxMKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwwKSwgYnJlYWtzID0gc2VxKDAsIDI0LCAzKSkgKwogIHNjYWxlX3lfZGlzY3JldGUoZXhwYW5kID0gYygwLDApKSArIAogIHNjYWxlX2ZpbGxfZGlzdGlsbGVyKHR5cGUgPSAiZGl2IiwgcGFsZXR0ZSA9ICJSZEJ1IiwgZGlyZWN0aW9uID0gLTEsIGxpbWl0cyA9IGMoLTEsIDEpICogbWF4KGFicyh0cmlhbHNfY2xvc2VkX2RpZmZbY291cnNlICVpbiUgYygiRW5nbGlzaCIsICJGcmVuY2giKSxdJHRyaWFsc19ub3JtYWxpc2VkX2Nsb3NlZCkpKSArCiAgY29vcmRfZml4ZWQoKSArCiAgdGhlbWVfcGFwZXIKCnBfaGVhdG1hcF9kaWZmCmBgYAoKCk1ha2UgYSBjb21iaW5lZCBwbG90IGZvciBpbiB0aGUgcGFwZXI6CmBgYHtyfQpwX2hlYXRtYXBfbGVnZW5kIDwtIGdldF9sZWdlbmQocF9oZWF0bWFwKQpwX2hlYXRtYXBfZGlmZl9sZWdlbmQgPC0gZ2V0X2xlZ2VuZChwX2hlYXRtYXBfZGlmZikKCnBfaGVhdG1hcCA8LSBwX2hlYXRtYXAgKyBndWlkZXMoZmlsbCA9IEZBTFNFKQpwX2hlYXRtYXBfZGlmZiA8LSBwX2hlYXRtYXBfZGlmZiArIGd1aWRlcyhmaWxsID0gRkFMU0UpCmBgYAoKYGBge3J9CnBsb3RfZ3JpZCgKICBwbG90X2dyaWQocF9oZWF0bWFwLCBwX2hlYXRtYXBfZGlmZiwKICAgICAgICAgIG5jb2wgPSAxLAogICAgICAgICAgbGFiZWxzID0gYygiQSIsICJCIiksCiAgICAgICAgICByZWxfaGVpZ2h0cyA9IGMoMSwgLjY1NSkKICAgICAgICAgICksCiAgcGxvdF9ncmlkKHBfaGVhdG1hcF9sZWdlbmQsIHBfaGVhdG1hcF9kaWZmX2xlZ2VuZCwKICAgICAgICAgICAgbmNvbCA9IDEsCiAgICAgICAgICAgIGFsaWduID0gInZoIiwgYXhpcyA9ICJscnRiIiksCiAgbmNvbCA9IDIsCiAgcmVsX3dpZHRocyA9IGMoMSwgLjE1KSkKCmdnc2F2ZSgiLi4vb3V0cHV0L2NvbWJpX2hlYXRtYXAucGRmIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA0LjUpCmdnc2F2ZSgiLi4vb3V0cHV0L2NvbWJpX2hlYXRtYXAuZXBzIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA0LjUpCmdnc2F2ZSgiLi4vb3V0cHV0L2NvbWJpX2hlYXRtYXAucG5nIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA0LjUpCmBgYAoKCiMjIEFjdGl2aXR5IHN0cmF0aWZpZWQgYnkgeWVhciBhbmQgbGV2ZWwKCmBgYHtyfQpkYiA8LSBkYl9jb25uZWN0KCkKY291bnRzX3N0cmF0IDwtIGRiR2V0UXVlcnkoZGIsIlNFTEVDVCByLm1ldGhvZCBBUyAnbWV0aG9kJywKICAgICAgICAgICAgICAgICAgICAgICAgICByLmJvb2tfaW5mb19pZCBhcyAnYm9va19pbmZvX2lkJywKICAgICAgICAgICAgICAgICAgICAgICAgICBEQVRFKHIuZGF0ZSArIDM2MDAsICd1bml4ZXBvY2gnKSBBUyAnZG95JywKICAgICAgICAgICAgICAgICAgICAgICAgICByLnVzZXJfaWQgQVMgJ3VzZXInLAogICAgICAgICAgICAgICAgICAgICAgICAgIENPVU5UKCopIEFTICd0cmlhbHMnCiAgICAgICAgICAgICAgICAgICAgICAgICAgRlJPTSAncmVzcG9uc2VzX25vZHVwbGljYXRlcycgcgogICAgICAgICAgICAgICAgICAgICAgICAgIEdST1VQIEJZIHIubWV0aG9kLAogICAgICAgICAgICAgICAgICAgICAgICAgIHIuYm9va19pbmZvX2lkLAogICAgICAgICAgICAgICAgICAgICAgICAgIERBVEUoci5kYXRlICsgMzYwMCwgJ3VuaXhlcG9jaCcpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHIudXNlcl9pZAogICAgICAgICAgICAgICAgICAgICAgICAiKQpkYl9kaXNjb25uZWN0KGRiKQoKc2V0RFQoY291bnRzX3N0cmF0KQpgYGAKCmBgYHtyfQpkYiA8LSBkYl9jb25uZWN0KCkKYm9va19pbmZvIDwtIGRiR2V0UXVlcnkoZGIsICJTRUxFQ1QgKiBGUk9NICdib29rX2luZm8nIikKZGJfZGlzY29ubmVjdChkYikKCnNldERUKGJvb2tfaW5mbykKYGBgCgpBZGQgYm9vayBpbmZvcm1hdGlvbjoKYGBge3J9CmNvdW50c19zdHJhdFtib29rX2luZm8sIG9uID0gImJvb2tfaW5mb19pZCIsIGMoImJvb2tfdGl0bGUiLCAibWV0aG9kX2dyb3VwIikgOj0gLihpLmJvb2tfdGl0bGUsIGkubWV0aG9kX2dyb3VwKV0KYGBgCgpBZGQgYSBzY2hvb2wgeWVhciBjb2x1bW4gKGN1dG9mZiBkYXRlOiAxIEF1Z3VzdCk6CmBgYHtyfQpjb3VudHNfc3RyYXRbLCBkb3lfcG9zaXggOj0gYXMuUE9TSVhjdChkb3kpXQpjb3VudHNfc3RyYXRbLCBzY2hvb2xfeWVhciA6PSBpZmVsc2UoZG95X3Bvc2l4IDwgIjIwMTktMDgtMDEiLCAiMTgvMTkiLCAiMTkvMjAiKV0KYGBgCgpBZGQgc2Vuc2libGUgY291cnNlIG5hbWVzOgpgYGB7cn0KY291bnRzX3N0cmF0WywgY291cnNlIDo9IGlmZWxzZShtZXRob2QgPT0gIkdyYW5kZXMgTGlnbmVzIiwgIkZyZW5jaCIsIGlmZWxzZShtZXRob2QgPT0gIlN0ZXBwaW5nIFN0b25lcyIsICJFbmdsaXNoIiwgIkdlcm1hbiIpKV0KYGBgCgpDb3VudCB0cmlhbHMgYnkgZGF5OgpgYGB7cn0KY291bnRzX3N0cmF0X2J5X2RheSA8LSBjb3VudHNfc3RyYXRbLCAuKHRyaWFsc190b3RhbCA9IHN1bSh0cmlhbHMsIG5hLnJtID0gVFJVRSkpLCBieSA9IC4oc2Nob29sX3llYXIsIGNvdXJzZSwgbWV0aG9kX2dyb3VwLCBib29rX3RpdGxlLCBkb3lfcG9zaXgpXQpzZXRvcmRlcihjb3VudHNfc3RyYXRfYnlfZGF5LCBzY2hvb2xfeWVhciwgY291cnNlLCBtZXRob2RfZ3JvdXAsIGJvb2tfdGl0bGUsIGRveV9wb3NpeCkKYGBgCgpTaW1wbGlmeSBsZXZlbCBuYW1lczoKYGBge3J9CiMgS2VlcCBhbGwgZGlzdGluY3Rpb25zCmNvdW50c19zdHJhdF9ieV9kYXlbLCBib29rX3RpdGxlX3NpbXBsZSA6PSBzdHJpbmdyOjpzdHJfc3ViKGJvb2tfdGl0bGUsIDMsIC0xMCldCmNvdW50c19zdHJhdF9ieV9kYXlbLCBib29rX3RpdGxlX3NpbXBsZSA6PSBmYWN0b3IoYm9va190aXRsZV9zaW1wbGUsIGxldmVscyA9IGMoInZtYm8gYi9sd29vIiwgInZtYm8gYiIsICJ2bWJvIGJrIiwgInZtYm8gayIsICJ2bWJvIGtndCIsICJ2bWJvLWd0IiwgInZtYm8gZ3QiLCAidm1iby1ndC9oYXZvIiwgInZtYm8gKHQpaHYiLCAiaGF2byIsICJoYXZvIHZ3byIsICJ2d28iKSldCgojIFNpbXBsaWZ5IHRvIHRocmVlIGxldmVscwpjb3VudHNfc3RyYXRfYnlfZGF5WywgbGV2ZWwgOj0gZHBseXI6OmNhc2Vfd2hlbigKICBncmVwbCggImh2IiwgYm9va190aXRsZSkgfiAiR2VuZXJhbCBzZWNvbmRhcnlcbihoYXZvKSIsCiAgZ3JlcGwoInZtYm8iLCBib29rX3RpdGxlKSB+ICJQcmUtdm9jYXRpb25hbFxuKHZtYm8pIiwKICBncmVwbCgiaGF2byIsIGJvb2tfdGl0bGUpIH4gIkdlbmVyYWwgc2Vjb25kYXJ5XG4oaGF2bykiLAogIGdyZXBsKCJ2d28iLCBib29rX3RpdGxlKSB+ICJQcmUtdW5pdmVyc2l0eVxuKHZ3bykiLAogIFRSVUUgfiAiT3RoZXIiKV0KY291bnRzX3N0cmF0X2J5X2RheVssIGxldmVsIDo9IGZhY3RvcihsZXZlbCwgbGV2ZWxzID0gYygiT3RoZXIiLCAiUHJlLXZvY2F0aW9uYWxcbih2bWJvKSIsICJHZW5lcmFsIHNlY29uZGFyeVxuKGhhdm8pIiwgIlByZS11bml2ZXJzaXR5XG4odndvKSIpKV0KYGBgCgpTaW1wbGlmeSB5ZWFyIG5hbWVzOgpgYGB7cn0KY291bnRzX3N0cmF0X2J5X2RheVssIHllYXIgOj0gZHBseXI6OmNhc2Vfd2hlbigKICBtZXRob2RfZ3JvdXAgPT0gIkxlZXJqYWFyIDEgKDVlIEVkLikiIH4gIlllYXIgMSIsCiAgbWV0aG9kX2dyb3VwID09ICJMZWVyamFhciAyICg1ZSBFZC4pIiB+ICJZZWFyIDIiLAogIG1ldGhvZF9ncm91cCA9PSAiTGVlcmphYXIgMyAoNWUgRWQuKSIgfiAiWWVhciAzIiwKICBtZXRob2RfZ3JvdXAgPT0gIkxlZXJqYWFyIDMvNCAoNWUgRWQuKSIgfiAiWWVhciAzLzQiLAogIG1ldGhvZF9ncm91cCA9PSAiTGVlcmphYXIgNCAoNWUgRWQuKSIgfiAiWWVhciA0IiwKICBtZXRob2RfZ3JvdXAgPT0gIlR3ZWVkZSBGYXNlICg2ZSBFZC4pIiB+ICJUd2VlZGUgRmFzZSIsCiAgVFJVRSB+ICJPdGhlciIpXQpgYGAKCgpBbGlnbiBzY2hvb2wgeWVhcnM6CmBgYHtyfQpjb3VudHNfc3RyYXRfYnlfZGF5W3NjaG9vbF95ZWFyID09ICIxOC8xOSIsIGRveV9wb3NpeF9hbGlnbmVkIDo9IGFzLlBPU0lYY3QoZG95X3Bvc2l4ICsgMzY1KjI0KjYwKjYwLCBvcmlnaW4gPSAiMTk3MC0wMS0wMSIpXQpjb3VudHNfc3RyYXRfYnlfZGF5W3NjaG9vbF95ZWFyID09ICIxOS8yMCIsIGRveV9wb3NpeF9hbGlnbmVkIDo9IGRveV9wb3NpeF0KYGBgCgpVc2UgY3V0LkRhdGUoKSB0byBiaW4gZGF0ZXMgYnkgd2Vlay4gRWFjaCBkYXkgaXMgYXNzaWduZWQgdGhlIGRhdGUgb2YgdGhlIG1vc3QgcmVjZW50IE1vbmRheS4KYGBge3J9CmNvdW50c19zdHJhdF9ieV9kYXlbLCBkb3lfcG9zaXhfYWxpZ25lZF93ZWVrIDo9IGN1dC5QT1NJWHQoZG95X3Bvc2l4X2FsaWduZWQsICJ3ZWVrIildCmNvdW50c19zdHJhdF9ieV9kYXlbLCB0cmlhbHNfdG90YWxfd2VlayA6PSBzdW0odHJpYWxzX3RvdGFsLCBuYS5ybSA9IFRSVUUpLCBieSA9IC4oc2Nob29sX3llYXIsIGNvdXJzZSwgbWV0aG9kX2dyb3VwLCBib29rX3RpdGxlX3NpbXBsZSwgZG95X3Bvc2l4X2FsaWduZWRfd2VlayldCgpjb3VudHNfc3RyYXRfYnlfZGF5WywgdHJpYWxzX3RvdGFsX3dlZWtfbGV2ZWwgOj0gc3VtKHRyaWFsc190b3RhbCksIGJ5ID0gLihzY2hvb2xfeWVhciwgY291cnNlLCBtZXRob2RfZ3JvdXAsIGxldmVsLCBkb3lfcG9zaXhfYWxpZ25lZF93ZWVrKV0KYGBgCgoKU3VtbWFyaXNlIGluY3JlYXNlIGR1cmluZyBsb2NrZG93bjoKYGBge3J9CmNvdW50c19zdHJhdF9pbmNyZWFzZSA8LSBjb3VudHNfc3RyYXRfYnlfZGF5W2JldHdlZW4oZG95X3Bvc2l4X2FsaWduZWQsIGRhdGVfc2Nob29sc19jbG9zZWQsIGRhdGVfc2Nob29sc19vcGVuZWQpLCAuKHRyaWFsc19sb2NrZG93biA9IHN1bSh0cmlhbHNfdG90YWwpKSwgYnkgPSAuKGNvdXJzZSwgYm9va190aXRsZV9zaW1wbGUsIG1ldGhvZF9ncm91cCwgeWVhciwgc2Nob29sX3llYXIpXQpjb3VudHNfc3RyYXRfaW5jcmVhc2VbLCBpbmNyZWFzZSA6PSB0cmlhbHNfbG9ja2Rvd25bMl0vdHJpYWxzX2xvY2tkb3duWzFdLCBieSA9IC4oY291cnNlLCBib29rX3RpdGxlX3NpbXBsZSwgbWV0aG9kX2dyb3VwLCB5ZWFyKV0KY291bnRzX3N0cmF0X2luY3JlYXNlWywgaW5jcmVhc2VfcGN0IDo9IHBhc3RlMCgiQ2hhbmdlOlxuIiwgc2NhbGVzOjpwZXJjZW50KGluY3JlYXNlLCBhY2N1cmFjeSA9IDIpKV0KCmNvdW50c19zdHJhdF9pbmNyZWFzZV9sZXZlbCA8LSBjb3VudHNfc3RyYXRfYnlfZGF5W2JldHdlZW4oZG95X3Bvc2l4X2FsaWduZWQsIGRhdGVfc2Nob29sc19jbG9zZWQsIGRhdGVfc2Nob29sc19vcGVuZWQpLCAuKHRyaWFsc19sb2NrZG93biA9IHN1bSh0cmlhbHNfdG90YWwpKSwgYnkgPSAuKGNvdXJzZSwgbGV2ZWwsIG1ldGhvZF9ncm91cCwgeWVhciwgc2Nob29sX3llYXIpXQpjb3VudHNfc3RyYXRfaW5jcmVhc2VfbGV2ZWxbLCBpbmNyZWFzZSA6PSB0cmlhbHNfbG9ja2Rvd25bMl0vdHJpYWxzX2xvY2tkb3duWzFdLCBieSA9IC4oY291cnNlLCBsZXZlbCwgbWV0aG9kX2dyb3VwLCB5ZWFyKV0KY291bnRzX3N0cmF0X2luY3JlYXNlX2xldmVsWywgaW5jcmVhc2VfcGN0IDo9IHBhc3RlMCgiQ2hhbmdlOlxuIiwgc2NhbGVzOjpwZXJjZW50KGluY3JlYXNlLCBhY2N1cmFjeSA9IDIpKV0KYGBgCgoKIyMjIEZyZW5jaApgYGB7cn0KZ2dwbG90KGNvdW50c19zdHJhdF9ieV9kYXlbY291cnNlID09ICJGcmVuY2giXSwgCiAgICAgICBhZXMoZ3JvdXAgPSBzY2hvb2xfeWVhciwgY29sb3VyID0gc2Nob29sX3llYXIsIGZpbGwgPSBzY2hvb2xfeWVhcikpICsKICBmYWNldF9ncmlkKGJvb2tfdGl0bGVfc2ltcGxlIH4gbWV0aG9kX2dyb3VwKSArCiAgZ2VvbV9yZWN0KHhtaW4gPSBkYXRlX3NjaG9vbHNfY2xvc2VkLCB4bWF4ID0gZGF0ZV9zY2hvb2xzX29wZW5lZCwgeW1pbiA9IC0yZTUsIHltYXggPSAyLjJlNiwgZmlsbCA9ICJncmV5OTIiLCBjb2xvdXIgPSAiZ3JleTUwIiwgbHR5ID0gMikgKwogIGdlb21fcmliYm9uKGFlcyh4ID0gZG95X3Bvc2l4X2FsaWduZWQsIHltaW4gPSAwLCB5bWF4ID0gdHJpYWxzX3RvdGFsX3dlZWssICksIGFscGhhID0gLjIpICsKICBnZW9tX3RleHQoZGF0YSA9IGNvdW50c19zdHJhdF9pbmNyZWFzZVtjb3Vyc2UgPT0gIkZyZW5jaCIgJiBzY2hvb2xfeWVhciA9PSAiMTkvMjAiXSwgCiAgICAgICAgICAgIGFlcyhsYWJlbCA9IGluY3JlYXNlX3BjdCksCiAgICAgICAgICAgIHggPSBhcy5QT1NJWGN0KChhcy5udW1lcmljKGRhdGVfc2Nob29sc19jbG9zZWQpICsgYXMubnVtZXJpYyhkYXRlX3NjaG9vbHNfb3BlbmVkKSkvMiwgb3JpZ2luID0gIjE5NzAtMDEtMDEiKSwKICAgICAgICAgICAgeSA9IDMuNmU1LAogICAgICAgICAgICBjb2xvdXIgPSAiYmxhY2siLAogICAgICAgICAgICB2anVzdCA9IDEsCiAgICAgICAgICAgIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV94X2RhdGV0aW1lKGV4cGFuZCA9IGMoMCwgMCksIAogICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYXMuUE9TSVhjdChjKAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0wOS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0xMS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wMS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wMy0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNy0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBhcy5QT1NJWGN0KGMoIjIwMTktMDktMDEgMDI6MDA6MDAgQ0VUIiwgIjIwMjAtMDctMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoMCwgMy43NWU1KSwgbGFiZWxzID0gbnVtYmVyX2Zvcm1hdCkgKwogIHNjYWxlX2NvbG91cl92aXJpZGlzX2QoZW5kID0gLjUsIGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKGVuZCA9IC41LCBkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJUcmlhbHMgcGVyIHdlZWsiLAogICAgICAgY29sb3VyID0gIlNjaG9vbCB5ZWFyIiwKICAgICAgIGZpbGwgPSAiU2Nob29sIHllYXIiLAogICAgICAgdGl0bGUgPSAiRnJlbmNoIikgKwogIHRoZW1lX3BhcGVyCgpnZ3NhdmUoIi4uL291dHB1dC90cmlhbF9oaXN0X2ZyZW5jaC5wZGYiLCB3aWR0aCA9IDE0LCBoZWlnaHQgPSAxMCkKZ2dzYXZlKCIuLi9vdXRwdXQvdHJpYWxfaGlzdF9mcmVuY2guZXBzIiwgd2lkdGggPSAxNCwgaGVpZ2h0ID0gMTApCmdnc2F2ZSgiLi4vb3V0cHV0L3RyaWFsX2hpc3RfZnJlbmNoLnBuZyIsIHdpZHRoID0gMTQsIGhlaWdodCA9IDEwKQpgYGAKClN0cmVhbWxpbmVkIHZlcnNpb24gZm9yIGluIHRoZSBwYXBlcjoKYGBge3J9CmdncGxvdChjb3VudHNfc3RyYXRfYnlfZGF5W2NvdXJzZSA9PSAiRnJlbmNoIl0sIAogICAgICAgYWVzKGdyb3VwID0gc2Nob29sX3llYXIsIGNvbG91ciA9IHNjaG9vbF95ZWFyLCBmaWxsID0gc2Nob29sX3llYXIpKSArCiAgZmFjZXRfZ3JpZChsZXZlbCB+IHllYXIpICsKICBnZW9tX3JlY3QoeG1pbiA9IGRhdGVfc2Nob29sc19jbG9zZWQsIHhtYXggPSBkYXRlX3NjaG9vbHNfb3BlbmVkLCB5bWluID0gLTJlNSwgeW1heCA9IDIuMmU2LCBmaWxsID0gImdyZXk5MiIsIGNvbG91ciA9ICJncmV5NTAiLCBsdHkgPSAyKSArCiAgZ2VvbV9yaWJib24oYWVzKHggPSBkb3lfcG9zaXhfYWxpZ25lZCwgeW1pbiA9IDAsIHltYXggPSB0cmlhbHNfdG90YWxfd2Vla19sZXZlbCwgKSwgYWxwaGEgPSAuMikgKwogIGdlb21fdGV4dChkYXRhID0gY291bnRzX3N0cmF0X2luY3JlYXNlX2xldmVsW2NvdXJzZSA9PSAiRnJlbmNoIiAmIHNjaG9vbF95ZWFyID09ICIxOS8yMCJdLCAKICAgICAgICAgICAgYWVzKGxhYmVsID0gaW5jcmVhc2VfcGN0KSwKICAgICAgICAgICAgeCA9IGFzLlBPU0lYY3QoKGFzLm51bWVyaWMoZGF0ZV9zY2hvb2xzX2Nsb3NlZCkgKyBhcy5udW1lcmljKGRhdGVfc2Nob29sc19vcGVuZWQpKS8yLCBvcmlnaW4gPSAiMTk3MC0wMS0wMSIpLAogICAgICAgICAgICB5ID0gMy42ZTUsCiAgICAgICAgICAgIGNvbG91ciA9ICJibGFjayIsCiAgICAgICAgICAgIHZqdXN0ID0gMSwKICAgICAgICAgICAgc2l6ZSA9IHJlbCgyLjc1KSwKICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHNjYWxlX3hfZGF0ZXRpbWUoZXhwYW5kID0gYygwLCAwKSwgCiAgICAgICAgICAgICAgICAgICBicmVha3MgPSBhcy5QT1NJWGN0KGMoCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTEwLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTEyLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTAyLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA0LTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA2LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGFzLlBPU0lYY3QoYygiMjAxOS0wOS0wMSAwMjowMDowMCBDRVQiLCAiMjAyMC0wNy0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwgbGltaXRzID0gYygwLCAzLjc1ZTUpLCBsYWJlbHMgPSBudW1iZXJfZm9ybWF0KSArCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfZChlbmQgPSAuNSwgZGlyZWN0aW9uID0gLTEsIG5hLnRyYW5zbGF0ZSA9IEZBTFNFKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoZW5kID0gLjUsIGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIlRyaWFscyBwZXIgd2VlayIsCiAgICAgICBjb2xvdXIgPSAiU2Nob29sIHllYXIiLAogICAgICAgZmlsbCA9ICJTY2hvb2wgeWVhciIpICsKICB0aGVtZV9wYXBlcgoKZ2dzYXZlKCIuLi9vdXRwdXQvdHJpYWxfaGlzdF9mcmVuY2hfbGV2ZWwucGRmIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA1KQpnZ3NhdmUoIi4uL291dHB1dC90cmlhbF9oaXN0X2ZyZW5jaF9sZXZlbC5lcHMiLCB3aWR0aCA9IDksIGhlaWdodCA9IDUpCmdnc2F2ZSgiLi4vb3V0cHV0L3RyaWFsX2hpc3RfZnJlbmNoX2xldmVsLnBuZyIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gNSkKYGBgCgpUaGVyZSBhcmUgdHdvIHJlYXNvbnMgd2h5IHRvdGFsIHRyaWFsIGNvdW50IG1heSBpbmNyZWFzZTogdGhlcmUgYXJlIG1vcmUgYWN0aXZlIHVzZXJzLCBvciBhY3RpdmUgdXNlcnMgY29tcGxldGUgbW9yZSB0cmlhbHMuCkdpdmVuIHRoZSBmaXhlZCBjdXJyaWN1bHVtLCB0aGUgZmlyc3QgcmVhc29uIHNlZW1zIG1vcmUgcGxhdXNpYmxlLgpXZSBjYW4gY29uZmlybSB0aGlzIGJ5IHBsb3R0aW5nIHRoZSB3ZWVrbHkgbnVtYmVyIG9mIHRyaWFscyBwZXIgdXNlci4KCmBgYHtyfQp1c2Vyc19zdHJhdCA8LSBjb3B5KGNvdW50c19zdHJhdCkKCnVzZXJzX3N0cmF0WywgYm9va190aXRsZV9zaW1wbGUgOj0gc3RyaW5ncjo6c3RyX3N1Yihib29rX3RpdGxlLCAzLCAtMTApXQp1c2Vyc19zdHJhdFssIGJvb2tfdGl0bGVfc2ltcGxlIDo9IGZhY3Rvcihib29rX3RpdGxlX3NpbXBsZSwgbGV2ZWxzID0gYygidm1ibyBiL2x3b28iLCAidm1ibyBiIiwgInZtYm8gYmsiLCAidm1ibyBrIiwgInZtYm8ga2d0IiwgInZtYm8tZ3QiLCAidm1ibyBndCIsICJ2bWJvLWd0L2hhdm8iLCAidm1ibyAodClodiIsICJoYXZvIiwgImhhdm8gdndvIiwgInZ3byIpKV0KCiMgU2ltcGxpZnkgdG8gdGhyZWUgbGV2ZWxzCnVzZXJzX3N0cmF0WywgbGV2ZWwgOj0gZHBseXI6OmNhc2Vfd2hlbigKICBncmVwbCggImh2IiwgYm9va190aXRsZSkgfiAiR2VuZXJhbCBzZWNvbmRhcnlcbihoYXZvKSIsCiAgZ3JlcGwoInZtYm8iLCBib29rX3RpdGxlKSB+ICJQcmUtdm9jYXRpb25hbFxuKHZtYm8pIiwKICBncmVwbCgiaGF2byIsIGJvb2tfdGl0bGUpIH4gIkdlbmVyYWwgc2Vjb25kYXJ5XG4oaGF2bykiLAogIGdyZXBsKCJ2d28iLCBib29rX3RpdGxlKSB+ICJQcmUtdW5pdmVyc2l0eVxuKHZ3bykiLAogIFRSVUUgfiAiT3RoZXIiKV0KdXNlcnNfc3RyYXRbLCBsZXZlbCA6PSBmYWN0b3IobGV2ZWwsIGxldmVscyA9IGMoIk90aGVyIiwgIlByZS12b2NhdGlvbmFsXG4odm1ibykiLCAiR2VuZXJhbCBzZWNvbmRhcnlcbihoYXZvKSIsICJQcmUtdW5pdmVyc2l0eVxuKHZ3bykiKSldCgp1c2Vyc19zdHJhdFssIHllYXIgOj0gZHBseXI6OmNhc2Vfd2hlbigKICBtZXRob2RfZ3JvdXAgPT0gIkxlZXJqYWFyIDEgKDVlIEVkLikiIH4gIlllYXIgMSIsCiAgbWV0aG9kX2dyb3VwID09ICJMZWVyamFhciAyICg1ZSBFZC4pIiB+ICJZZWFyIDIiLAogIG1ldGhvZF9ncm91cCA9PSAiTGVlcmphYXIgMyAoNWUgRWQuKSIgfiAiWWVhciAzIiwKICBtZXRob2RfZ3JvdXAgPT0gIkxlZXJqYWFyIDMvNCAoNWUgRWQuKSIgfiAiWWVhciAzLzQiLAogIG1ldGhvZF9ncm91cCA9PSAiTGVlcmphYXIgNCAoNWUgRWQuKSIgfiAiWWVhciA0IiwKICBtZXRob2RfZ3JvdXAgPT0gIlR3ZWVkZSBGYXNlICg2ZSBFZC4pIiB+ICJUd2VlZGUgRmFzZSIsCiAgVFJVRSB+ICJPdGhlciIpXQoKdXNlcnNfc3RyYXRbc2Nob29sX3llYXIgPT0gIjE4LzE5IiwgZG95X3Bvc2l4X2FsaWduZWQgOj0gYXMuUE9TSVhjdChkb3lfcG9zaXggKyAzNjUqMjQqNjAqNjAsIG9yaWdpbiA9ICIxOTcwLTAxLTAxIildCnVzZXJzX3N0cmF0W3NjaG9vbF95ZWFyID09ICIxOS8yMCIsIGRveV9wb3NpeF9hbGlnbmVkIDo9IGRveV9wb3NpeF0KdXNlcnNfc3RyYXRbLCBkb3lfcG9zaXhfYWxpZ25lZF93ZWVrIDo9IGN1dC5QT1NJWHQoZG95X3Bvc2l4X2FsaWduZWQsICJ3ZWVrIildCnVzZXJzX3N0cmF0X2J5X3dlZWsgPC0gdXNlcnNfc3RyYXRbLCAuKHVzZXJzX3dlZWtfbGV2ZWwgPSAuTiksIGJ5ID0gLihzY2hvb2xfeWVhciwgY291cnNlLCB5ZWFyLCBsZXZlbCwgZG95X3Bvc2l4X2FsaWduZWRfd2VlayldCmBgYAoKYGBge3J9CmNvdW50c19zdHJhdF9ieV9kYXkgPC0gY291bnRzX3N0cmF0X2J5X2RheVt1c2Vyc19zdHJhdF9ieV93ZWVrLCBvbiA9IGMoInNjaG9vbF95ZWFyIiwgImNvdXJzZSIsICJ5ZWFyIiwgImxldmVsIiwgImRveV9wb3NpeF9hbGlnbmVkX3dlZWsiKV1bLCB0cmlhbHNfcGVyX3VzZXJfd2VlayA6PSB0cmlhbHNfdG90YWxfd2Vla19sZXZlbCAvIHVzZXJzX3dlZWtfbGV2ZWxdCmBgYAoKYGBge3J9CmdncGxvdChjb3VudHNfc3RyYXRfYnlfZGF5W2NvdXJzZSA9PSAiRnJlbmNoIl0sIAogICAgICAgYWVzKGdyb3VwID0gc2Nob29sX3llYXIsIGNvbG91ciA9IHNjaG9vbF95ZWFyLCBmaWxsID0gc2Nob29sX3llYXIpKSArCiAgZmFjZXRfZ3JpZChsZXZlbCB+IHllYXIpICsKICBnZW9tX3JlY3QoeG1pbiA9IGRhdGVfc2Nob29sc19jbG9zZWQsIHhtYXggPSBkYXRlX3NjaG9vbHNfb3BlbmVkLCB5bWluID0gLTJlNSwgeW1heCA9IDIuMmU2LCBmaWxsID0gImdyZXk5MiIsIGNvbG91ciA9ICJncmV5NTAiLCBsdHkgPSAyKSArCiAgZ2VvbV9yaWJib24oYWVzKHggPSBkb3lfcG9zaXhfYWxpZ25lZCwgeW1pbiA9IDAsIHltYXggPSB0cmlhbHNfcGVyX3VzZXJfd2VlaywgKSwgYWxwaGEgPSAuMikgKwogIHNjYWxlX3hfZGF0ZXRpbWUoZXhwYW5kID0gYygwLCAwKSwgCiAgICAgICAgICAgICAgICAgICBicmVha3MgPSBhcy5QT1NJWGN0KGMoCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTEwLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTEyLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTAyLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA0LTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA2LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGFzLlBPU0lYY3QoYygiMjAxOS0wOS0wMSAwMjowMDowMCBDRVQiLCAiMjAyMC0wNy0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwgbGFiZWxzID0gbnVtYmVyX2Zvcm1hdCkgKwogIHNjYWxlX2NvbG91cl92aXJpZGlzX2QoZW5kID0gLjUsIGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKGVuZCA9IC41LCBkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJUcmlhbHMgcGVyIHVzZXIgcGVyIHdlZWsiLAogICAgICAgY29sb3VyID0gIlNjaG9vbCB5ZWFyIiwKICAgICAgIGZpbGwgPSAiU2Nob29sIHllYXIiKSArCiAgdGhlbWVfcGFwZXIKCmBgYAoKQXMgZXhwZWN0ZWQsIHRoZXJlIGlzIGVzc2VudGlhbGx5IG5vIGNoYW5nZSBpbiB0aGUgbnVtYmVyIG9mIHRyaWFscyBlYWNoIHVzZXIgY29tcGxldGVzLCBzbyB0aGUgaW5jcmVhc2VkIHRyaWFsIGNvdW50IHRoYXQgd2Ugc2VlIGNvbWVzIGZyb20gbW9yZSB1c2VycyBiZWluZyBhY3RpdmUuCgoKCiMjIyBFbmdsaXNoCgpgYGB7cn0KZ2dwbG90KGNvdW50c19zdHJhdF9ieV9kYXlbY291cnNlID09ICJFbmdsaXNoIl0sIAogICAgICAgYWVzKGdyb3VwID0gc2Nob29sX3llYXIsIGNvbG91ciA9IHNjaG9vbF95ZWFyLCBmaWxsID0gc2Nob29sX3llYXIpKSArCiAgZmFjZXRfZ3JpZChib29rX3RpdGxlX3NpbXBsZSB+IG1ldGhvZF9ncm91cCkgKwogIGdlb21fcmVjdCh4bWluID0gZGF0ZV9zY2hvb2xzX2Nsb3NlZCwgeG1heCA9IGRhdGVfc2Nob29sc19vcGVuZWQsIHltaW4gPSAtMmU1LCB5bWF4ID0gMi4yZTYsIGZpbGwgPSAiZ3JleTkyIiwgY29sb3VyID0gImdyZXk1MCIsIGx0eSA9IDIpICsKICBnZW9tX3JpYmJvbihhZXMoeCA9IGRveV9wb3NpeF9hbGlnbmVkLCB5bWluID0gMCwgeW1heCA9IHRyaWFsc190b3RhbF93ZWVrLCApLCBhbHBoYSA9IC4yKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBjb3VudHNfc3RyYXRfaW5jcmVhc2VbY291cnNlID09ICJFbmdsaXNoIiAmIHNjaG9vbF95ZWFyID09ICIxOS8yMCJdLCAKICAgICAgICAgICAgYWVzKGxhYmVsID0gaW5jcmVhc2VfcGN0KSwKICAgICAgICAgICAgeCA9IGFzLlBPU0lYY3QoKGFzLm51bWVyaWMoZGF0ZV9zY2hvb2xzX2Nsb3NlZCkgKyBhcy5udW1lcmljKGRhdGVfc2Nob29sc19vcGVuZWQpKS8yLCBvcmlnaW4gPSAiMTk3MC0wMS0wMSIpLAogICAgICAgICAgICB5ID0gMy42ZTUsCiAgICAgICAgICAgIGNvbG91ciA9ICJibGFjayIsCiAgICAgICAgICAgIHZqdXN0ID0gMSwKICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHNjYWxlX3hfZGF0ZXRpbWUoZXhwYW5kID0gYygwLCAwKSwgCiAgICAgICAgICAgICAgICAgICBicmVha3MgPSBhcy5QT1NJWGN0KGMoCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTA5LTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTExLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTAxLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTAzLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA1LTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA3LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGFzLlBPU0lYY3QoYygiMjAxOS0wOS0wMSAwMjowMDowMCBDRVQiLCAiMjAyMC0wNy0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwgbGltaXRzID0gYygwLCAzLjc1ZTUpLCBsYWJlbHMgPSBudW1iZXJfZm9ybWF0KSArCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfZChlbmQgPSAuNSwgZGlyZWN0aW9uID0gLTEsIG5hLnRyYW5zbGF0ZSA9IEZBTFNFKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoZW5kID0gLjUsIGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIlRyaWFscyBwZXIgd2VlayIsCiAgICAgICBjb2xvdXIgPSAiU2Nob29sIHllYXIiLAogICAgICAgZmlsbCA9ICJTY2hvb2wgeWVhciIsCiAgICAgICB0aXRsZSA9ICJFbmdsaXNoIikgKwogIHRoZW1lX3BhcGVyCgpnZ3NhdmUoIi4uL291dHB1dC90cmlhbF9oaXN0X2VuZ2xpc2gucGRmIiwgd2lkdGggPSAxNCwgaGVpZ2h0ID0gMTApCmdnc2F2ZSgiLi4vb3V0cHV0L3RyaWFsX2hpc3RfZW5nbGlzaC5lcHMiLCB3aWR0aCA9IDE0LCBoZWlnaHQgPSAxMCkKZ2dzYXZlKCIuLi9vdXRwdXQvdHJpYWxfaGlzdF9lbmdsaXNoLnBuZyIsIHdpZHRoID0gMTQsIGhlaWdodCA9IDEwKQpgYGAKClN0cmVhbWxpbmVkIHZlcnNpb24gZm9yIGluIHRoZSBwYXBlcjoKYGBge3J9CmdncGxvdChjb3VudHNfc3RyYXRfYnlfZGF5W2NvdXJzZSA9PSAiRW5nbGlzaCIgJiBsZXZlbCAhPSAiT3RoZXIiXSwgCiAgICAgICBhZXMoZ3JvdXAgPSBzY2hvb2xfeWVhciwgY29sb3VyID0gc2Nob29sX3llYXIsIGZpbGwgPSBzY2hvb2xfeWVhcikpICsKICBmYWNldF9ncmlkKGxldmVsIH4geWVhcikgKwogIGdlb21fcmVjdCh4bWluID0gZGF0ZV9zY2hvb2xzX2Nsb3NlZCwgeG1heCA9IGRhdGVfc2Nob29sc19vcGVuZWQsIHltaW4gPSAtMmU1LCB5bWF4ID0gMi4yZTYsIGZpbGwgPSAiZ3JleTkyIiwgY29sb3VyID0gImdyZXk1MCIsIGx0eSA9IDIpICsKICBnZW9tX3JpYmJvbihhZXMoeCA9IGRveV9wb3NpeF9hbGlnbmVkLCB5bWluID0gMCwgeW1heCA9IHRyaWFsc190b3RhbF93ZWVrX2xldmVsLCApLCBhbHBoYSA9IC4yKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBjb3VudHNfc3RyYXRfaW5jcmVhc2VfbGV2ZWxbY291cnNlID09ICJFbmdsaXNoIiAmIGxldmVsICE9ICJPdGhlciIgJiBzY2hvb2xfeWVhciA9PSAiMTkvMjAiXSwgCiAgICAgICAgICAgIGFlcyhsYWJlbCA9IGluY3JlYXNlX3BjdCksCiAgICAgICAgICAgIHggPSBhcy5QT1NJWGN0KChhcy5udW1lcmljKGRhdGVfc2Nob29sc19jbG9zZWQpICsgYXMubnVtZXJpYyhkYXRlX3NjaG9vbHNfb3BlbmVkKSkvMiwgb3JpZ2luID0gIjE5NzAtMDEtMDEiKSwKICAgICAgICAgICAgeSA9IDkuNmU1LAogICAgICAgICAgICBjb2xvdXIgPSAiYmxhY2siLAogICAgICAgICAgICB2anVzdCA9IDEsCiAgICAgICAgICAgIHNpemUgPSByZWwoMi43NSksCiAgICAgICAgICAgIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV94X2RhdGV0aW1lKGV4cGFuZCA9IGMoMCwgMCksIAogICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYXMuUE9TSVhjdChjKAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0xMC0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0xMi0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wMi0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNC0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNi0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBhcy5QT1NJWGN0KGMoIjIwMTktMDktMDEgMDI6MDA6MDAgQ0VUIiwgIjIwMjAtMDctMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoMCwgMWU2KSwgbGFiZWxzID0gbnVtYmVyX2Zvcm1hdCkgKwogIHNjYWxlX2NvbG91cl92aXJpZGlzX2QoZW5kID0gLjUsIGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKGVuZCA9IC41LCBkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJUcmlhbHMgcGVyIHdlZWsiLAogICAgICAgY29sb3VyID0gIlNjaG9vbCB5ZWFyIiwKICAgICAgIGZpbGwgPSAiU2Nob29sIHllYXIiKSArCiAgdGhlbWVfcGFwZXIKCmdnc2F2ZSgiLi4vb3V0cHV0L3RyaWFsX2hpc3RfZW5nbGlzaF9sZXZlbC5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDUpCmdnc2F2ZSgiLi4vb3V0cHV0L3RyaWFsX2hpc3RfZW5nbGlzaF9sZXZlbC5lcHMiLCB3aWR0aCA9IDksIGhlaWdodCA9IDUpCmdnc2F2ZSgiLi4vb3V0cHV0L3RyaWFsX2hpc3RfZW5nbGlzaF9sZXZlbC5wbmciLCB3aWR0aCA9IDksIGhlaWdodCA9IDUpCmBgYAoKQWxzbyBwbG90IHRoZSB3ZWVrbHkgbnVtYmVyIG9mIHRyaWFscyBwZXIgdXNlci4KQXMgd2l0aCBGcmVuY2gsIHRoaXMgbnVtYmVyIHN0YXlzIG1vcmUgb3IgbGVzcyBjb25zdGFudDoKYGBge3J9CmdncGxvdChjb3VudHNfc3RyYXRfYnlfZGF5W2NvdXJzZSA9PSAiRW5nbGlzaCJdLCAKICAgICAgIGFlcyhncm91cCA9IHNjaG9vbF95ZWFyLCBjb2xvdXIgPSBzY2hvb2xfeWVhciwgZmlsbCA9IHNjaG9vbF95ZWFyKSkgKwogIGZhY2V0X2dyaWQobGV2ZWwgfiB5ZWFyKSArCiAgZ2VvbV9yZWN0KHhtaW4gPSBkYXRlX3NjaG9vbHNfY2xvc2VkLCB4bWF4ID0gZGF0ZV9zY2hvb2xzX29wZW5lZCwgeW1pbiA9IC0yZTUsIHltYXggPSAyLjJlNiwgZmlsbCA9ICJncmV5OTIiLCBjb2xvdXIgPSAiZ3JleTUwIiwgbHR5ID0gMikgKwogIGdlb21fcmliYm9uKGFlcyh4ID0gZG95X3Bvc2l4X2FsaWduZWQsIHltaW4gPSAwLCB5bWF4ID0gdHJpYWxzX3Blcl91c2VyX3dlZWssICksIGFscGhhID0gLjIpICsKICBzY2FsZV94X2RhdGV0aW1lKGV4cGFuZCA9IGMoMCwgMCksIAogICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYXMuUE9TSVhjdChjKAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0xMC0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0xMi0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wMi0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNC0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNi0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBhcy5QT1NJWGN0KGMoIjIwMTktMDktMDEgMDI6MDA6MDAgQ0VUIiwgIjIwMjAtMDctMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxhYmVscyA9IG51bWJlcl9mb3JtYXQpICsKICBzY2FsZV9jb2xvdXJfdmlyaWRpc19kKGVuZCA9IC41LCBkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZChlbmQgPSAuNSwgZGlyZWN0aW9uID0gLTEsIG5hLnRyYW5zbGF0ZSA9IEZBTFNFKSArCiAgbGFicyh4ID0gTlVMTCwKICAgICAgIHkgPSAiVHJpYWxzIHBlciB1c2VyIHBlciB3ZWVrIiwKICAgICAgIGNvbG91ciA9ICJTY2hvb2wgeWVhciIsCiAgICAgICBmaWxsID0gIlNjaG9vbCB5ZWFyIikgKwogIHRoZW1lX3BhcGVyCgpgYGAKCgojIyBRdWVzdGlvbiB0eXBlCgpUaGVyZSBhcmUgZGlmZmVyZW50IHF1ZXN0aW9uIGZvcm1hdHM6IG9wZW4tYW5zd2VyLCBpbiB3aGljaCB0aGUgc3R1ZGVudCB0eXBlcyB0aGUgYW5zd2VyLCBhbmQgbXVsdGlwbGUtY2hvaWNlLCBpbiB3aGljaCB0aGUgc3R1ZGVudCBzZWxlY3RzIHRoZSBhbnN3ZXIgZnJvbSBhIHNldCBvZiAzIG9yIDQgb3B0aW9ucy4KCmBgYHtyfQpkYiA8LSBkYl9jb25uZWN0KCkKcXVlc3Rpb25fdHlwZSA8LSBkYkdldFF1ZXJ5KGRiLCAKICAgICAgICAgICAgICAgICAgICAgICJTRUxFQ1Qgci5tZXRob2QgQVMgJ21ldGhvZCcsCiAgICAgICAgICAgICAgICAgICAgICBEQVRFKHIuZGF0ZSArIDM2MDAsICd1bml4ZXBvY2gnKSBBUyAnZG95JywKICAgICAgICAgICAgICAgICAgICAgIHIuY2hvaWNlcyBBUyAnY2hvaWNlcycsCiAgICAgICAgICAgICAgICAgICAgICBDT1VOVCgqKSBBUyAnbicKICAgICAgICAgICAgICAgICAgICAgIEZST00gJ3Jlc3BvbnNlc19ub2R1cGxpY2F0ZXMnIHIKICAgICAgICAgICAgICAgICAgICAgIFdIRVJFIHIuc3R1ZHkgPT0gMAogICAgICAgICAgICAgICAgICAgICAgR1JPVVAgQlkgci5tZXRob2QsCiAgICAgICAgICAgICAgICAgICAgICBEQVRFKHIuZGF0ZSArIDM2MDAsICd1bml4ZXBvY2gnKSwKICAgICAgICAgICAgICAgICAgICAgIHIuY2hvaWNlcyIKKQpzZXREVChxdWVzdGlvbl90eXBlKQpkYl9kaXNjb25uZWN0KGRiKQpgYGAKCkFkZCBhIHNjaG9vbCB5ZWFyIGNvbHVtbiAoY3V0b2ZmIGRhdGU6IDEgQXVndXN0KToKYGBge3J9CnF1ZXN0aW9uX3R5cGVbLCBkb3lfcG9zaXggOj0gYXMuUE9TSVhjdChkb3kpXQpxdWVzdGlvbl90eXBlWywgc2Nob29sX3llYXIgOj0gaWZlbHNlKGRveV9wb3NpeCA8ICIyMDE5LTA4LTAxIiwgIjE4LzE5IiwgIjE5LzIwIildCmBgYAoKQWRkIHNlbnNpYmxlIGNvdXJzZSBuYW1lczoKYGBge3J9CnF1ZXN0aW9uX3R5cGVbLCBjb3Vyc2UgOj0gaWZlbHNlKG1ldGhvZCA9PSAiR3JhbmRlcyBMaWduZXMiLCAiRnJlbmNoIiwgaWZlbHNlKG1ldGhvZCA9PSAiU3RlcHBpbmcgU3RvbmVzIiwgIkVuZ2xpc2giLCAiR2VybWFuIikpXQpgYGAKCkFsaWduIHNjaG9vbCB5ZWFyczoKYGBge3J9CnF1ZXN0aW9uX3R5cGVbc2Nob29sX3llYXIgPT0gIjE4LzE5IiwgZG95X3Bvc2l4X2FsaWduZWQgOj0gYXMuUE9TSVhjdChkb3lfcG9zaXggKyAzNjUqMjQqNjAqNjAsIG9yaWdpbiA9ICIxOTcwLTAxLTAxIildCnF1ZXN0aW9uX3R5cGVbc2Nob29sX3llYXIgPT0gIjE5LzIwIiwgZG95X3Bvc2l4X2FsaWduZWQgOj0gZG95X3Bvc2l4XQpgYGAKClVzZSBjdXQuRGF0ZSgpIHRvIGJpbiBkYXRlcyBieSB3ZWVrLiBFYWNoIGRheSBpcyBhc3NpZ25lZCB0aGUgZGF0ZSBvZiB0aGUgbW9zdCByZWNlbnQgTW9uZGF5LgpgYGB7cn0KcXVlc3Rpb25fdHlwZVssIGRveV9wb3NpeF93ZWVrIDo9IGN1dC5QT1NJWHQoZG95X3Bvc2l4LCAid2VlayIpXQpxdWVzdGlvbl90eXBlWywgZG95X3Bvc2l4X2FsaWduZWRfd2VlayA6PSBjdXQuUE9TSVh0KGRveV9wb3NpeF9hbGlnbmVkLCAid2VlayIpXQpgYGAKCmBgYHtyfQpxdWVzdGlvbl90eXBlX2J5X3dlZWsgPC0gcXVlc3Rpb25fdHlwZVssIC4obiA9IHN1bShuKSksIGJ5ID0gLihjb3Vyc2UsIHNjaG9vbF95ZWFyLCBkb3lfcG9zaXhfYWxpZ25lZF93ZWVrLCBjaG9pY2VzKV0KYGBgCgpgYGB7cn0KZ2dwbG90KHF1ZXN0aW9uX3R5cGVfYnlfd2Vla1tjb3Vyc2UgJWluJSBjKCJFbmdsaXNoIiwgIkZyZW5jaCIpXSwgYWVzKHggPSBhcy5QT1NJWGN0KGRveV9wb3NpeF9hbGlnbmVkX3dlZWspLCB5ID0gbiwgZ3JvdXAgPSBpbnRlcmFjdGlvbihzY2hvb2xfeWVhcixhcy5mYWN0b3IoY2hvaWNlcykpLCBjb2xvdXIgPSBzY2hvb2xfeWVhcikpICsKICBmYWNldF9ncmlkKGNvdXJzZSB+IGNob2ljZXMpICsKICBnZW9tX2xpbmUoKSArCiAgc2NhbGVfeF9kYXRldGltZShleHBhbmQgPSBjKDAsIDApLAogICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYXMuUE9TSVhjdChjKAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0xMC0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0xMi0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wMi0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNC0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNi0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBhcy5QT1NJWGN0KGMoIjIwMTktMDktMDEgMDI6MDA6MDAgQ0VUIiwgIjIwMjAtMDctMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgbGFicyh4ID0gTlVMTCwKICAgICAgIHkgPSAiVHJpYWxzIiwKICAgICAgIGNvbG91ciA9ICJTY2hvb2wgeWVhciIpICsKICB0aGVtZV9wYXBlcgoKYGBgCgpgYGB7cn0KcXVlc3Rpb25fdHlwZVssIC4obiA9IHN1bShuKSksIGJ5ID0gLihjb3Vyc2UsIG1jcSA9IGNob2ljZXM+MSwgc2Nob29sX3llYXIpXVssIC4ocGVyY19tY3EgPSBuW21jcSA9PSBUUlVFXS9zdW0obikpLCBieSA9IC4oY291cnNlLCBzY2hvb2xfeWVhcildCmBgYAoKVGhlcmUgaXMgYSBjbGVhciBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGxhbmd1YWdlcyBpbiB0aGUgcXVlc3Rpb24gZm9ybWF0IHVzZWQ6IEVuZ2xpc2ggdXNlcyBhbG1vc3QgZXhjbHVzaXZlbHkgNC1hbHRlcm5hdGl2ZSBNQ1FzLCB3aGlsZSBGcmVuY2ggdXNlcyBhIG1peCBvZiBNQ1FzIChpbmNsdWRpbmcgYSBzbWFsbCBudW1iZXIgb2YgMy1hbHRlcm5hdGl2ZSBxdWVzdGlvbnMpIGFuZCBvcGVuLWFuc3dlciBxdWVzdGlvbnMuCgoKIyBTZXNzaW9uIGluZm8KYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCgo=